Browse Source

Merge pull request #1 from asciimoo/master

-
Apply55gx 7 years ago
parent
commit
d800e3fcfa
49 changed files with 754 additions and 1473 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. 1
    1
      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. 1
    8
      searx/static/themes/oscar/css/logicodev.min.css
  24. 1
    1
      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. 56
    6
      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 View File

1
+comment: false
2
+coverage:
3
+  status:
4
+    project:
5
+      default:
6
+        # basic
7
+        target: auto
8
+        threshold: null
9
+        base: auto 
10
+        # advanced
11
+        branches: null
12
+        if_no_uploads: error
13
+        if_not_found: success
14
+        if_ci_failed: error
15
+        only_pulls: false
16
+        flags: null
17
+        paths: null
18
+    patch:
19
+      default:
20
+        # basic
21
+        target: auto
22
+        threshold: null
23
+        base: auto 
24
+        # advanced
25
+        branches: null
26
+        if_no_uploads: error
27
+        if_not_found: success
28
+        if_ci_failed: error
29
+        only_pulls: false
30
+        flags: null
31
+        paths: null

+ 3
- 3
.travis.yml View File

13
 before_install:
13
 before_install:
14
   - "export DISPLAY=:99.0"
14
   - "export DISPLAY=:99.0"
15
   - "sh -e /etc/init.d/xvfb start"
15
   - "sh -e /etc/init.d/xvfb start"
16
-  - npm install less less-plugin-clean-css grunt-cli
16
+  - npm install less@2.7 less-plugin-clean-css grunt-cli
17
   - export PATH=`pwd`/node_modules/.bin:$PATH
17
   - export PATH=`pwd`/node_modules/.bin:$PATH
18
   - ./manage.sh install_geckodriver ~/drivers
18
   - ./manage.sh install_geckodriver ~/drivers
19
   - export PATH=~/drivers:$PATH
19
   - export PATH=~/drivers:$PATH
20
 install:
20
 install:
21
   - ./manage.sh npm_packages
21
   - ./manage.sh npm_packages
22
   - ./manage.sh update_dev_packages
22
   - ./manage.sh update_dev_packages
23
-  - pip install coveralls
23
+  - pip install codecov
24
 script:
24
 script:
25
   - ./manage.sh styles
25
   - ./manage.sh styles
26
   - ./manage.sh grunt_build
26
   - ./manage.sh grunt_build
27
   - ./manage.sh tests
27
   - ./manage.sh tests
28
 after_success:
28
 after_success:
29
   - ./manage.sh py_test_coverage
29
   - ./manage.sh py_test_coverage
30
-  - coveralls
30
+  - codecov
31
 notifications:
31
 notifications:
32
   irc:
32
   irc:
33
     channels:
33
     channels:

+ 9
- 3
README.rst View File

9
 
9
 
10
 See the `documentation <https://asciimoo.github.io/searx>`__ and the `wiki <https://github.com/asciimoo/searx/wiki>`__ for more information.
10
 See the `documentation <https://asciimoo.github.io/searx>`__ and the `wiki <https://github.com/asciimoo/searx/wiki>`__ for more information.
11
 
11
 
12
-|Flattr searx|
12
+|OpenCollective searx backers|
13
+|OpenCollective searx sponsors|
13
 
14
 
14
 Installation
15
 Installation
15
 ~~~~~~~~~~~~
16
 ~~~~~~~~~~~~
41
 -  `twitter <https://twitter.com/Searx_engine>`__
42
 -  `twitter <https://twitter.com/Searx_engine>`__
42
 -  IRC: #searx @ freenode
43
 -  IRC: #searx @ freenode
43
 
44
 
44
-.. |Flattr searx| image:: http://api.flattr.com/button/flattr-badge-large.png
45
-   :target: https://flattr.com/submit/auto?user_id=asciimoo&url=https://github.com/asciimoo/searx&title=searx&language=&tags=github&category=software
45
+
46
+.. |OpenCollective searx backers| image:: https://opencollective.com/searx/backers/badge.svg
47
+   :target: https://opencollective.com/searx#backer
48
+
49
+
50
+.. |OpenCollective searx sponsors| image:: https://opencollective.com/searx/sponsors/badge.svg
51
+   :target: https://opencollective.com/searx#sponsor

+ 37
- 37
manage.sh View File

1
 #!/bin/sh
1
 #!/bin/sh
2
 
2
 
3
-BASE_DIR=$(dirname "`readlink -f "$0"`")
4
-PYTHONPATH=$BASE_DIR
3
+BASE_DIR="$(dirname -- "`readlink -f -- "$0"`")"
4
+PYTHONPATH="$BASE_DIR"
5
 SEARX_DIR="$BASE_DIR/searx"
5
 SEARX_DIR="$BASE_DIR/searx"
6
-ACTION=$1
6
+ACTION="$1"
7
 
7
 
8
-cd "$BASE_DIR"
8
+cd -- "$BASE_DIR"
9
 
9
 
10
 update_packages() {
10
 update_packages() {
11
     pip install --upgrade pip
11
     pip install --upgrade pip
22
     echo '[!] Checking geckodriver'
22
     echo '[!] Checking geckodriver'
23
     # TODO : check the current geckodriver version
23
     # TODO : check the current geckodriver version
24
     set -e
24
     set -e
25
-    geckodriver -V 2>1 > /dev/null || NOTFOUND=1
25
+    geckodriver -V > /dev/null 2>&1 || NOTFOUND=1
26
     set +e
26
     set +e
27
-    if [ -z $NOTFOUND ]; then
28
-	return
27
+    if [ -z "$NOTFOUND" ]; then
28
+        return
29
     fi
29
     fi
30
     GECKODRIVER_VERSION="v0.18.0"
30
     GECKODRIVER_VERSION="v0.18.0"
31
-    PLATFORM=`python -c "import six; import platform; six.print_(platform.system().lower(), platform.architecture()[0])"`
32
-    case $PLATFORM in
33
-	"linux 32bit" | "linux2 32bit") ARCH="linux32";;
34
-	"linux 64bit" | "linux2 64bit") ARCH="linux64";;
35
-	"windows 32 bit") ARCH="win32";;
36
-	"windows 64 bit") ARCH="win64";;
37
-	"mac 64bit") ARCH="macos";;
31
+    PLATFORM="`python -c "import six; import platform; six.print_(platform.system().lower(), platform.architecture()[0])"`"
32
+    case "$PLATFORM" in
33
+        "linux 32bit" | "linux2 32bit") ARCH="linux32";;
34
+        "linux 64bit" | "linux2 64bit") ARCH="linux64";;
35
+        "windows 32 bit") ARCH="win32";;
36
+        "windows 64 bit") ARCH="win64";;
37
+        "mac 64bit") ARCH="macos";;
38
     esac
38
     esac
39
     GECKODRIVER_URL="https://github.com/mozilla/geckodriver/releases/download/$GECKODRIVER_VERSION/geckodriver-$GECKODRIVER_VERSION-$ARCH.tar.gz";
39
     GECKODRIVER_URL="https://github.com/mozilla/geckodriver/releases/download/$GECKODRIVER_VERSION/geckodriver-$GECKODRIVER_VERSION-$ARCH.tar.gz";
40
 
40
 
41
     if [ -z "$1" ]; then
41
     if [ -z "$1" ]; then
42
-	if [ -z "$VIRTUAL_ENV" ]; then
43
-	    echo "geckodriver can't be installed because VIRTUAL_ENV is not set, you should download it from\n  $GECKODRIVER_URL"
44
-	    exit    
45
-	else
46
-	    GECKODRIVER_DIR="$VIRTUAL_ENV/bin"
47
-	fi
42
+        if [ -z "$VIRTUAL_ENV" ]; then
43
+            echo "geckodriver can't be installed because VIRTUAL_ENV is not set, you should download it from\n  $GECKODRIVER_URL"
44
+            exit
45
+        else
46
+            GECKODRIVER_DIR="$VIRTUAL_ENV/bin"
47
+        fi
48
     else
48
     else
49
-	GECKODRIVER_DIR="$1"
50
-	mkdir -p "$GECKODRIVER_DIR"
49
+        GECKODRIVER_DIR="$1"
50
+        mkdir -p -- "$GECKODRIVER_DIR"
51
     fi
51
     fi
52
 
52
 
53
     echo "Installing $GECKODRIVER_DIR/geckodriver from\n  $GECKODRIVER_URL"
53
     echo "Installing $GECKODRIVER_DIR/geckodriver from\n  $GECKODRIVER_URL"
54
-    
55
-    FILE=`mktemp`
56
-    wget "$GECKODRIVER_URL" -qO $FILE && tar xz -C "$GECKODRIVER_DIR" -f $FILE geckodriver
57
-    rm $FILE
58
-    chmod 777 "$GECKODRIVER_DIR/geckodriver"
54
+
55
+    FILE="`mktemp`"
56
+    wget -qO "$FILE" -- "$GECKODRIVER_URL" && tar xz -C "$GECKODRIVER_DIR" -f "$FILE" geckodriver
57
+    rm -- "$FILE"
58
+    chmod 777 -- "$GECKODRIVER_DIR/geckodriver"
59
 }
59
 }
60
 
60
 
61
 pep8_check() {
61
 pep8_check() {
73
 
73
 
74
 py_test_coverage() {
74
 py_test_coverage() {
75
     echo '[!] Running python test coverage'
75
     echo '[!] Running python test coverage'
76
-    PYTHONPATH=`pwd` python -m nose2 -C --coverage "$SEARX_DIR" -s "$BASE_DIR/tests/unit"
77
-    coverage report
78
-    coverage html
76
+    PYTHONPATH="`pwd`" python -m nose2 -C --log-capture --with-coverage --coverage "$SEARX_DIR" -s "$BASE_DIR/tests/unit" \
77
+    && coverage report \
78
+    && coverage html
79
 }
79
 }
80
 
80
 
81
 robot_tests() {
81
 robot_tests() {
82
     echo '[!] Running robot tests'
82
     echo '[!] Running robot tests'
83
-    PYTHONPATH=`pwd` python "$SEARX_DIR/testing.py" robot
83
+    PYTHONPATH="`pwd`" python "$SEARX_DIR/testing.py" robot
84
 }
84
 }
85
 
85
 
86
 tests() {
86
 tests() {
113
 
113
 
114
 npm_packages() {
114
 npm_packages() {
115
     echo '[!] install NPM packages for oscar theme'
115
     echo '[!] install NPM packages for oscar theme'
116
-    cd $BASE_DIR/searx/static/themes/oscar
116
+    cd -- "$BASE_DIR/searx/static/themes/oscar"
117
     npm install
117
     npm install
118
 
118
 
119
-    echo '[!] install NPM packages for simple theme'    
120
-    cd $BASE_DIR/searx/static/themes/simple
119
+    echo '[!] install NPM packages for simple theme'
120
+    cd -- "$BASE_DIR/searx/static/themes/simple"
121
     npm install
121
     npm install
122
 }
122
 }
123
 
123
 
124
 grunt_build() {
124
 grunt_build() {
125
     echo '[!] Grunt build : oscar theme'
125
     echo '[!] Grunt build : oscar theme'
126
     grunt --gruntfile "$SEARX_DIR/static/themes/oscar/gruntfile.js"
126
     grunt --gruntfile "$SEARX_DIR/static/themes/oscar/gruntfile.js"
127
-    echo '[!] Grunt build : simple theme'    
127
+    echo '[!] Grunt build : simple theme'
128
     grunt --gruntfile "$SEARX_DIR/static/themes/simple/gruntfile.js"
128
     grunt --gruntfile "$SEARX_DIR/static/themes/simple/gruntfile.js"
129
 }
129
 }
130
 
130
 
133
 }
133
 }
134
 
134
 
135
 help() {
135
 help() {
136
-    [ -z "$1" ] || printf "Error: $1\n"
136
+    [ -z "$1" ] || printf 'Error: %s\n' "$1"
137
     echo "Searx manage.sh help
137
     echo "Searx manage.sh help
138
 
138
 
139
 Commands
139
 Commands
156
 
156
 
157
 [ "$(command -V "$ACTION" | grep ' function$')" = "" ] \
157
 [ "$(command -V "$ACTION" | grep ' function$')" = "" ] \
158
     && help "action not found" \
158
     && help "action not found" \
159
-    || $ACTION "$2"
159
+    || "$ACTION" "$2"

+ 1
- 0
requirements-dev.txt View File

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

+ 1
- 1
requirements.txt View File

7
 pyopenssl==17.2.0
7
 pyopenssl==17.2.0
8
 python-dateutil==2.6.1
8
 python-dateutil==2.6.1
9
 pyyaml==3.12
9
 pyyaml==3.12
10
-requests[socks]==2.14.2
10
+requests[socks]==2.18.4

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


+ 47
- 9
searx/engines/bing_images.py View File

18
 from lxml import html
18
 from lxml import html
19
 from json import loads
19
 from json import loads
20
 import re
20
 import re
21
-from searx.engines.bing import _fetch_supported_languages, supported_languages_url
22
 from searx.url_utils import urlencode
21
 from searx.url_utils import urlencode
23
 
22
 
24
 # engine dependent config
23
 # engine dependent config
26
 paging = True
25
 paging = True
27
 safesearch = True
26
 safesearch = True
28
 time_range_support = True
27
 time_range_support = True
28
+language_support = True
29
+supported_languages_url = 'https://www.bing.com/account/general'
29
 
30
 
30
 # search-url
31
 # search-url
31
 base_url = 'https://www.bing.com/'
32
 base_url = 'https://www.bing.com/'
45
 _quote_keys_regex = re.compile('({|,)([a-z][a-z0-9]*):(")', re.I | re.U)
46
 _quote_keys_regex = re.compile('({|,)([a-z][a-z0-9]*):(")', re.I | re.U)
46
 
47
 
47
 
48
 
49
+# get supported region code
50
+def get_region_code(lang, lang_list=None):
51
+    region = None
52
+    if lang in (lang_list or supported_languages):
53
+        region = lang
54
+    elif lang.startswith('no'):
55
+        region = 'nb-NO'
56
+    else:
57
+        # try to get a supported country code with language
58
+        lang = lang.split('-')[0]
59
+        for lc in (lang_list or supported_languages):
60
+            if lang == lc.split('-')[0]:
61
+                region = lc
62
+                break
63
+    if region:
64
+        return region.lower()
65
+    else:
66
+        return 'en-us'
67
+
68
+
48
 # do search-request
69
 # do search-request
49
 def request(query, params):
70
 def request(query, params):
50
     offset = (params['pageno'] - 1) * 10 + 1
71
     offset = (params['pageno'] - 1) * 10 + 1
51
 
72
 
52
-    # required for cookie
53
-    if params['language'] == 'all':
54
-        language = 'en-US'
55
-    else:
56
-        language = params['language']
57
-
58
     search_path = search_string.format(
73
     search_path = search_string.format(
59
         query=urlencode({'q': query}),
74
         query=urlencode({'q': query}),
60
         offset=offset)
75
         offset=offset)
61
 
76
 
77
+    language = get_region_code(params['language'])
78
+
62
     params['cookies']['SRCHHPGUSR'] = \
79
     params['cookies']['SRCHHPGUSR'] = \
63
-        'NEWWND=0&NRSLT=-1&SRCHLANG=' + language.split('-')[0] +\
64
-        '&ADLT=' + safesearch_types.get(params['safesearch'], 'DEMOTE')
80
+        'ADLT=' + safesearch_types.get(params['safesearch'], 'DEMOTE')
81
+
82
+    params['cookies']['_EDGE_S'] = 'mkt=' + language +\
83
+        '&ui=' + language + '&F=1'
65
 
84
 
66
     params['url'] = base_url + search_path
85
     params['url'] = base_url + search_path
67
     if params['time_range'] in time_range_dict:
86
     if params['time_range'] in time_range_dict:
106
 
125
 
107
     # return results
126
     # return results
108
     return results
127
     return results
128
+
129
+
130
+# get supported languages from their site
131
+def _fetch_supported_languages(resp):
132
+    supported_languages = []
133
+    dom = html.fromstring(resp.text)
134
+
135
+    regions_xpath = '//div[@id="region-section-content"]' \
136
+                    + '//ul[@class="b_vList"]/li/a/@href'
137
+
138
+    regions = dom.xpath(regions_xpath)
139
+    for region in regions:
140
+        code = re.search('setmkt=[^\&]+', region).group()[7:]
141
+        if code == 'nb-NO':
142
+            code = 'no-NO'
143
+
144
+        supported_languages.append(code)
145
+
146
+    return supported_languages

+ 4
- 1
searx/engines/bing_videos.py View File

12
 
12
 
13
 from json import loads
13
 from json import loads
14
 from lxml import html
14
 from lxml import html
15
+from searx.engines.bing_images import _fetch_supported_languages, supported_languages_url, get_region_code
15
 from searx.engines.xpath import extract_text
16
 from searx.engines.xpath import extract_text
16
 from searx.url_utils import urlencode
17
 from searx.url_utils import urlencode
17
 
18
 
21
 safesearch = True
22
 safesearch = True
22
 time_range_support = True
23
 time_range_support = True
23
 number_of_results = 10
24
 number_of_results = 10
25
+language_support = True
24
 
26
 
25
 search_url = 'https://www.bing.com/videos/asyncv2?{query}&async=content&'\
27
 search_url = 'https://www.bing.com/videos/asyncv2?{query}&async=content&'\
26
              'first={offset}&count={number_of_results}&CW=1366&CH=25&FORM=R5VR5'
28
              'first={offset}&count={number_of_results}&CW=1366&CH=25&FORM=R5VR5'
45
         'ADLT=' + safesearch_types.get(params['safesearch'], 'DEMOTE')
47
         'ADLT=' + safesearch_types.get(params['safesearch'], 'DEMOTE')
46
 
48
 
47
     # language cookie
49
     # language cookie
48
-    params['cookies']['_EDGE_S'] = 'mkt=' + params['language'].lower() + '&F=1'
50
+    region = get_region_code(params['language'], lang_list=supported_languages)
51
+    params['cookies']['_EDGE_S'] = 'mkt=' + region + '&F=1'
49
 
52
 
50
     # query and paging
53
     # query and paging
51
     params['url'] = search_url.format(query=urlencode({'q': query}),
54
     params['url'] = search_url.format(query=urlencode({'q': query}),

+ 0
- 70
searx/engines/blekko_images.py View File

1
-"""
2
- Blekko (Images)
3
-
4
- @website     https://blekko.com
5
- @provide-api yes (inofficial)
6
-
7
- @using-api   yes
8
- @results     JSON
9
- @stable      yes
10
- @parse       url, title, img_src
11
-"""
12
-
13
-from json import loads
14
-from searx.url_utils import urlencode
15
-
16
-# engine dependent config
17
-categories = ['images']
18
-paging = True
19
-safesearch = True
20
-
21
-# search-url
22
-base_url = 'https://blekko.com'
23
-search_url = '/api/images?{query}&c={c}'
24
-
25
-# safesearch definitions
26
-safesearch_types = {2: '1',
27
-                    1: '',
28
-                    0: '0'}
29
-
30
-
31
-# do search-request
32
-def request(query, params):
33
-    c = (params['pageno'] - 1) * 48
34
-
35
-    params['url'] = base_url +\
36
-        search_url.format(query=urlencode({'q': query}),
37
-                          c=c)
38
-
39
-    if params['pageno'] != 1:
40
-        params['url'] += '&page={pageno}'.format(pageno=(params['pageno'] - 1))
41
-
42
-    # let Blekko know we wan't have profiling
43
-    params['cookies']['tag_lesslogging'] = '1'
44
-
45
-    # parse safesearch argument
46
-    params['cookies']['safesearch'] = safesearch_types.get(params['safesearch'], '')
47
-
48
-    return params
49
-
50
-
51
-# get response from search-request
52
-def response(resp):
53
-    results = []
54
-
55
-    search_results = loads(resp.text)
56
-
57
-    # return empty array if there are no results
58
-    if not search_results:
59
-        return []
60
-
61
-    for result in search_results:
62
-        # append result
63
-        results.append({'url': result['page_url'],
64
-                        'title': result['title'],
65
-                        'content': '',
66
-                        'img_src': result['url'],
67
-                        'template': 'images.html'})
68
-
69
-    # return results
70
-    return results

+ 7
- 0
searx/engines/digg.py View File

10
  @parse       url, title, content, publishedDate, thumbnail
10
  @parse       url, title, content, publishedDate, thumbnail
11
 """
11
 """
12
 
12
 
13
+import random
14
+import string
13
 from dateutil import parser
15
 from dateutil import parser
14
 from json import loads
16
 from json import loads
15
 from lxml import html
17
 from lxml import html
30
 content_xpath = './/p//text()'
32
 content_xpath = './/p//text()'
31
 pubdate_xpath = './/time'
33
 pubdate_xpath = './/time'
32
 
34
 
35
+digg_cookie_chars = string.ascii_uppercase + string.ascii_lowercase +\
36
+    string.digits + "+_"
37
+
33
 
38
 
34
 # do search-request
39
 # do search-request
35
 def request(query, params):
40
 def request(query, params):
36
     offset = (params['pageno'] - 1) * 10
41
     offset = (params['pageno'] - 1) * 10
37
     params['url'] = search_url.format(position=offset,
42
     params['url'] = search_url.format(position=offset,
38
                                       query=quote_plus(query))
43
                                       query=quote_plus(query))
44
+    params['cookies']['frontend.auid'] = ''.join(random.choice(
45
+        digg_cookie_chars) for _ in range(22))
39
     return params
46
     return params
40
 
47
 
41
 
48
 

+ 1
- 1
searx/engines/duckduckgo.py View File

134
     regions_json = loads(response_page)
134
     regions_json = loads(response_page)
135
     supported_languages = map((lambda x: x[3:] + '-' + x[:2].upper()), regions_json.keys())
135
     supported_languages = map((lambda x: x[3:] + '-' + x[:2].upper()), regions_json.keys())
136
 
136
 
137
-    return supported_languages
137
+    return list(supported_languages)

+ 19
- 39
searx/engines/faroo.py View File

4
  @website     http://www.faroo.com
4
  @website     http://www.faroo.com
5
  @provide-api yes (http://www.faroo.com/hp/api/api.html), require API-key
5
  @provide-api yes (http://www.faroo.com/hp/api/api.html), require API-key
6
 
6
 
7
- @using-api   yes
7
+ @using-api   no
8
  @results     JSON
8
  @results     JSON
9
  @stable      yes
9
  @stable      yes
10
  @parse       url, title, content, publishedDate, img_src
10
  @parse       url, title, content, publishedDate, img_src
20
 paging = True
20
 paging = True
21
 language_support = True
21
 language_support = True
22
 number_of_results = 10
22
 number_of_results = 10
23
-api_key = None
24
 
23
 
25
 # search-url
24
 # search-url
26
 url = 'http://www.faroo.com/'
25
 url = 'http://www.faroo.com/'
27
-search_url = url + 'api?{query}'\
28
-                      '&start={offset}'\
29
-                      '&length={number_of_results}'\
30
-                      '&l={language}'\
31
-                      '&src={categorie}'\
32
-                      '&i=false'\
33
-                      '&f=json'\
34
-                      '&key={api_key}'  # noqa
26
+search_url = url + 'instant.json?{query}'\
27
+    '&start={offset}'\
28
+    '&length={number_of_results}'\
29
+    '&l={language}'\
30
+    '&src={categorie}'\
31
+    '&i=false'\
32
+    '&c=false'
35
 
33
 
36
 search_category = {'general': 'web',
34
 search_category = {'general': 'web',
37
                    'news': 'news'}
35
                    'news': 'news'}
57
                                       number_of_results=number_of_results,
55
                                       number_of_results=number_of_results,
58
                                       query=urlencode({'q': query}),
56
                                       query=urlencode({'q': query}),
59
                                       language=language,
57
                                       language=language,
60
-                                      categorie=categorie,
61
-                                      api_key=api_key)
58
+                                      categorie=categorie)
62
 
59
 
63
-    # using searx User-Agent
64
-    params['headers']['User-Agent'] = searx_useragent()
60
+    params['headers']['Referer'] = url
65
 
61
 
66
     return params
62
     return params
67
 
63
 
68
 
64
 
69
 # get response from search-request
65
 # get response from search-request
70
 def response(resp):
66
 def response(resp):
71
-    # HTTP-Code 401: api-key is not valide
72
-    if resp.status_code == 401:
73
-        raise Exception("API key is not valide")
74
-
75
     # HTTP-Code 429: rate limit exceeded
67
     # HTTP-Code 429: rate limit exceeded
76
     if resp.status_code == 429:
68
     if resp.status_code == 429:
77
         raise Exception("rate limit has been exceeded!")
69
         raise Exception("rate limit has been exceeded!")
86
 
78
 
87
     # parse results
79
     # parse results
88
     for result in search_res['results']:
80
     for result in search_res['results']:
81
+        publishedDate = None
82
+        result_json = {'url': result['url'], 'title': result['title'],
83
+                       'content': result['kwic']}
89
         if result['news']:
84
         if result['news']:
90
-            # timestamp (milliseconds since 1970)
91
-            publishedDate = datetime.datetime.fromtimestamp(result['date'] / 1000.0)  # noqa
92
-
93
-            # append news result
94
-            results.append({'url': result['url'],
95
-                            'title': result['title'],
96
-                            'publishedDate': publishedDate,
97
-                            'content': result['kwic']})
98
-
99
-        else:
100
-            # append general result
101
-            # TODO, publishedDate correct?
102
-            results.append({'url': result['url'],
103
-                            'title': result['title'],
104
-                            'content': result['kwic']})
85
+            result_json['publishedDate'] = \
86
+                datetime.datetime.fromtimestamp(result['date'] / 1000.0)
105
 
87
 
106
         # append image result if image url is set
88
         # append image result if image url is set
107
-        # TODO, show results with an image like in faroo
108
         if result['iurl']:
89
         if result['iurl']:
109
-            results.append({'template': 'images.html',
110
-                            'url': result['url'],
111
-                            'title': result['title'],
112
-                            'content': result['kwic'],
113
-                            'img_src': result['iurl']})
90
+            result_json['template'] = 'videos.html'
91
+            result_json['thumbnail'] = result['iurl']
92
+
93
+        results.append(result_json)
114
 
94
 
115
     # return results
95
     # return results
116
     return results
96
     return results

+ 0
- 62
searx/engines/generalfile.py View File

1
-"""
2
- General Files (Files)
3
-
4
- @website     http://www.general-files.org
5
- @provide-api no (nothing found)
6
-
7
- @using-api   no (because nothing found)
8
- @results     HTML (using search portal)
9
- @stable      no (HTML can change)
10
- @parse       url, title, content
11
-
12
- @todo        detect torrents?
13
-"""
14
-
15
-from lxml import html
16
-
17
-# engine dependent config
18
-categories = ['files']
19
-paging = True
20
-
21
-# search-url
22
-base_url = 'http://www.general-file.com'
23
-search_url = base_url + '/files-{letter}/{query}/{pageno}'
24
-
25
-# specific xpath variables
26
-result_xpath = '//table[@class="block-file"]'
27
-title_xpath = './/h2/a//text()'
28
-url_xpath = './/h2/a/@href'
29
-content_xpath = './/p//text()'
30
-
31
-
32
-# do search-request
33
-def request(query, params):
34
-
35
-    params['url'] = search_url.format(query=query,
36
-                                      letter=query[0],
37
-                                      pageno=params['pageno'])
38
-
39
-    return params
40
-
41
-
42
-# get response from search-request
43
-def response(resp):
44
-    results = []
45
-
46
-    dom = html.fromstring(resp.text)
47
-
48
-    # parse results
49
-    for result in dom.xpath(result_xpath):
50
-        url = result.xpath(url_xpath)[0]
51
-
52
-        # skip fast download links
53
-        if not url.startswith('/'):
54
-            continue
55
-
56
-        # append result
57
-        results.append({'url': base_url + url,
58
-                        'title': ''.join(result.xpath(title_xpath)),
59
-                        'content': ''.join(result.xpath(content_xpath))})
60
-
61
-    # return results
62
-    return results

+ 6
- 2
searx/engines/gigablast.py View File

10
  @parse       url, title, content
10
  @parse       url, title, content
11
 """
11
 """
12
 
12
 
13
+import random
13
 from json import loads
14
 from json import loads
14
 from time import time
15
 from time import time
15
 from lxml.html import fromstring
16
 from lxml.html import fromstring
32
     '&qh=0'\
33
     '&qh=0'\
33
     '&qlang={lang}'\
34
     '&qlang={lang}'\
34
     '&ff={safesearch}'\
35
     '&ff={safesearch}'\
35
-    '&rxikd={rxikd}'  # random number - 9 digits
36
+    '&rxieu={rxieu}'\
37
+    '&rand={rxikd}'  # current unix timestamp
36
 
38
 
37
 # specific xpath variables
39
 # specific xpath variables
38
 results_xpath = '//response//result'
40
 results_xpath = '//response//result'
59
     else:
61
     else:
60
         safesearch = 0
62
         safesearch = 0
61
 
63
 
64
+    # rxieu is some kind of hash from the search query, but accepts random atm
62
     search_path = search_string.format(query=urlencode({'q': query}),
65
     search_path = search_string.format(query=urlencode({'q': query}),
63
                                        offset=offset,
66
                                        offset=offset,
64
                                        number_of_results=number_of_results,
67
                                        number_of_results=number_of_results,
65
-                                       rxikd=str(time())[:9],
68
+                                       rxikd=int(time() * 1000),
69
+                                       rxieu=random.randint(1000000000, 9999999999),
66
                                        lang=language,
70
                                        lang=language,
67
                                        safesearch=safesearch)
71
                                        safesearch=safesearch)
68
 
72
 

+ 2
- 2
searx/engines/google_news.py View File

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

+ 40
- 49
searx/engines/nyaa.py View File

1
 """
1
 """
2
- Nyaa.se (Anime Bittorrent tracker)
2
+ Nyaa.si (Anime Bittorrent tracker)
3
 
3
 
4
- @website      http://www.nyaa.se/
4
+ @website      http://www.nyaa.si/
5
  @provide-api  no
5
  @provide-api  no
6
  @using-api    no
6
  @using-api    no
7
  @results      HTML
7
  @results      HTML
12
 from lxml import html
12
 from lxml import html
13
 from searx.engines.xpath import extract_text
13
 from searx.engines.xpath import extract_text
14
 from searx.url_utils import urlencode
14
 from searx.url_utils import urlencode
15
+from searx.utils import get_torrent_size, int_or_zero
15
 
16
 
16
 # engine dependent config
17
 # engine dependent config
17
 categories = ['files', 'images', 'videos', 'music']
18
 categories = ['files', 'images', 'videos', 'music']
18
 paging = True
19
 paging = True
19
 
20
 
20
 # search-url
21
 # search-url
21
-base_url = 'http://www.nyaa.se/'
22
+base_url = 'http://www.nyaa.si/'
22
 search_url = base_url + '?page=search&{query}&offset={offset}'
23
 search_url = base_url + '?page=search&{query}&offset={offset}'
23
 
24
 
24
 # xpath queries
25
 # xpath queries
25
-xpath_results = '//table[@class="tlist"]//tr[contains(@class, "tlistrow")]'
26
-xpath_category = './/td[@class="tlisticon"]/a'
27
-xpath_title = './/td[@class="tlistname"]/a'
28
-xpath_torrent_file = './/td[@class="tlistdownload"]/a'
29
-xpath_filesize = './/td[@class="tlistsize"]/text()'
30
-xpath_seeds = './/td[@class="tlistsn"]/text()'
31
-xpath_leeches = './/td[@class="tlistln"]/text()'
32
-xpath_downloads = './/td[@class="tlistdn"]/text()'
33
-
34
-
35
-# convert a variable to integer or return 0 if it's not a number
36
-def int_or_zero(num):
37
-    if isinstance(num, list):
38
-        if len(num) < 1:
39
-            return 0
40
-        num = num[0]
41
-    if num.isdigit():
42
-        return int(num)
43
-    return 0
44
-
45
-
46
-# get multiplier to convert torrent size to bytes
47
-def get_filesize_mul(suffix):
48
-    return {
49
-        'KB': 1024,
50
-        'MB': 1024 ** 2,
51
-        'GB': 1024 ** 3,
52
-        'TB': 1024 ** 4,
53
-
54
-        'KIB': 1024,
55
-        'MIB': 1024 ** 2,
56
-        'GIB': 1024 ** 3,
57
-        'TIB': 1024 ** 4
58
-    }[str(suffix).upper()]
26
+xpath_results = '//table[contains(@class, "torrent-list")]//tr[not(th)]'
27
+xpath_category = './/td[1]/a[1]'
28
+xpath_title = './/td[2]/a[last()]'
29
+xpath_torrent_links = './/td[3]/a'
30
+xpath_filesize = './/td[4]/text()'
31
+xpath_seeds = './/td[6]/text()'
32
+xpath_leeches = './/td[7]/text()'
33
+xpath_downloads = './/td[8]/text()'
59
 
34
 
60
 
35
 
61
 # do search-request
36
 # do search-request
72
     dom = html.fromstring(resp.text)
47
     dom = html.fromstring(resp.text)
73
 
48
 
74
     for result in dom.xpath(xpath_results):
49
     for result in dom.xpath(xpath_results):
50
+        # defaults
51
+        filesize = 0
52
+        magnet_link = ""
53
+        torrent_link = ""
54
+
75
         # category in which our torrent belongs
55
         # category in which our torrent belongs
76
-        category = result.xpath(xpath_category)[0].attrib.get('title')
56
+        try:
57
+            category = result.xpath(xpath_category)[0].attrib.get('title')
58
+        except:
59
+            pass
77
 
60
 
78
         # torrent title
61
         # torrent title
79
         page_a = result.xpath(xpath_title)[0]
62
         page_a = result.xpath(xpath_title)[0]
80
         title = extract_text(page_a)
63
         title = extract_text(page_a)
81
 
64
 
82
         # link to the page
65
         # link to the page
83
-        href = page_a.attrib.get('href')
66
+        href = base_url + page_a.attrib.get('href')
84
 
67
 
85
-        # link to the torrent file
86
-        torrent_link = result.xpath(xpath_torrent_file)[0].attrib.get('href')
87
-
88
-        # torrent size
89
-        try:
90
-            file_size, suffix = result.xpath(xpath_filesize)[0].split(' ')
91
-            file_size = int(float(file_size) * get_filesize_mul(suffix))
92
-        except:
93
-            file_size = None
68
+        for link in result.xpath(xpath_torrent_links):
69
+            url = link.attrib.get('href')
70
+            if 'magnet' in url:
71
+                # link to the magnet
72
+                magnet_link = url
73
+            else:
74
+                # link to the torrent file
75
+                torrent_link = url
94
 
76
 
95
         # seed count
77
         # seed count
96
         seed = int_or_zero(result.xpath(xpath_seeds))
78
         seed = int_or_zero(result.xpath(xpath_seeds))
101
         # torrent downloads count
83
         # torrent downloads count
102
         downloads = int_or_zero(result.xpath(xpath_downloads))
84
         downloads = int_or_zero(result.xpath(xpath_downloads))
103
 
85
 
86
+        # let's try to calculate the torrent size
87
+        try:
88
+            filesize_info = result.xpath(xpath_filesize)[0]
89
+            filesize, filesize_multiplier = filesize_info.split()
90
+            filesize = get_torrent_size(filesize, filesize_multiplier)
91
+        except:
92
+            pass
93
+
104
         # content string contains all information not included into template
94
         # content string contains all information not included into template
105
         content = 'Category: "{category}". Downloaded {downloads} times.'
95
         content = 'Category: "{category}". Downloaded {downloads} times.'
106
         content = content.format(category=category, downloads=downloads)
96
         content = content.format(category=category, downloads=downloads)
110
                         'content': content,
100
                         'content': content,
111
                         'seed': seed,
101
                         'seed': seed,
112
                         'leech': leech,
102
                         'leech': leech,
113
-                        'filesize': file_size,
103
+                        'filesize': filesize,
114
                         'torrentfile': torrent_link,
104
                         'torrentfile': torrent_link,
105
+                        'magnetlink': magnet_link,
115
                         'template': 'torrent.html'})
106
                         'template': 'torrent.html'})
116
 
107
 
117
     return results
108
     return results

+ 1
- 1
searx/engines/swisscows.py View File

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

+ 2
- 3
searx/engines/tokyotoshokan.py View File

14
 from lxml import html
14
 from lxml import html
15
 from searx.engines.xpath import extract_text
15
 from searx.engines.xpath import extract_text
16
 from datetime import datetime
16
 from datetime import datetime
17
-from searx.engines.nyaa import int_or_zero, get_filesize_mul
18
 from searx.url_utils import urlencode
17
 from searx.url_utils import urlencode
18
+from searx.utils import get_torrent_size, int_or_zero
19
 
19
 
20
 # engine dependent config
20
 # engine dependent config
21
 categories = ['files', 'videos', 'music']
21
 categories = ['files', 'videos', 'music']
76
                 try:
76
                 try:
77
                     # ('1.228', 'GB')
77
                     # ('1.228', 'GB')
78
                     groups = size_re.match(item).groups()
78
                     groups = size_re.match(item).groups()
79
-                    multiplier = get_filesize_mul(groups[1])
80
-                    params['filesize'] = int(multiplier * float(groups[0]))
79
+                    params['filesize'] = get_torrent_size(groups[0], groups[1])
81
                 except:
80
                 except:
82
                     pass
81
                     pass
83
             elif item.startswith('Date:'):
82
             elif item.startswith('Date:'):

+ 22
- 16
searx/engines/torrentz.py View File

1
 """
1
 """
2
- Torrentz.eu (BitTorrent meta-search engine)
2
+ Torrentz2.eu (BitTorrent meta-search engine)
3
 
3
 
4
- @website      https://torrentz.eu/
4
+ @website      https://torrentz2.eu/
5
  @provide-api  no
5
  @provide-api  no
6
 
6
 
7
  @using-api    no
7
  @using-api    no
14
 import re
14
 import re
15
 from lxml import html
15
 from lxml import html
16
 from datetime import datetime
16
 from datetime import datetime
17
-from searx.engines.nyaa import int_or_zero, get_filesize_mul
18
 from searx.engines.xpath import extract_text
17
 from searx.engines.xpath import extract_text
19
 from searx.url_utils import urlencode
18
 from searx.url_utils import urlencode
19
+from searx.utils import get_torrent_size
20
 
20
 
21
 # engine dependent config
21
 # engine dependent config
22
 categories = ['files', 'videos', 'music']
22
 categories = ['files', 'videos', 'music']
23
 paging = True
23
 paging = True
24
 
24
 
25
 # search-url
25
 # search-url
26
-# https://torrentz.eu/search?f=EXAMPLE&p=6
27
-base_url = 'https://torrentz.eu/'
26
+# https://torrentz2.eu/search?f=EXAMPLE&p=6
27
+base_url = 'https://torrentz2.eu/'
28
 search_url = base_url + 'search?{query}'
28
 search_url = base_url + 'search?{query}'
29
 
29
 
30
 
30
 
31
 # do search-request
31
 # do search-request
32
 def request(query, params):
32
 def request(query, params):
33
     page = params['pageno'] - 1
33
     page = params['pageno'] - 1
34
-    query = urlencode({'q': query, 'p': page})
34
+    query = urlencode({'f': query, 'p': page})
35
     params['url'] = search_url.format(query=query)
35
     params['url'] = search_url.format(query=query)
36
     return params
36
     return params
37
 
37
 
54
         # extract url and remove a slash in the beginning
54
         # extract url and remove a slash in the beginning
55
         link = links[0].attrib.get('href').lstrip('/')
55
         link = links[0].attrib.get('href').lstrip('/')
56
 
56
 
57
-        seed = result.xpath('./dd/span[@class="u"]/text()')[0].replace(',', '')
58
-        leech = result.xpath('./dd/span[@class="d"]/text()')[0].replace(',', '')
57
+        seed = 0
58
+        leech = 0
59
+        try:
60
+            seed = int(result.xpath('./dd/span[4]/text()')[0].replace(',', ''))
61
+            leech = int(result.xpath('./dd/span[5]/text()')[0].replace(',', ''))
62
+        except:
63
+            pass
59
 
64
 
60
         params = {
65
         params = {
61
             'url': base_url + link,
66
             'url': base_url + link,
62
             'title': title,
67
             'title': title,
63
-            'seed': int_or_zero(seed),
64
-            'leech': int_or_zero(leech),
68
+            'seed': seed,
69
+            'leech': leech,
65
             'template': 'torrent.html'
70
             'template': 'torrent.html'
66
         }
71
         }
67
 
72
 
68
         # let's try to calculate the torrent size
73
         # let's try to calculate the torrent size
69
         try:
74
         try:
70
-            size_str = result.xpath('./dd/span[@class="s"]/text()')[0]
71
-            size, suffix = size_str.split()
72
-            params['filesize'] = int(size) * get_filesize_mul(suffix)
75
+            filesize_info = result.xpath('./dd/span[3]/text()')[0]
76
+            filesize, filesize_multiplier = filesize_info.split()
77
+            filesize = get_torrent_size(filesize, filesize_multiplier)
78
+
79
+            params['filesize'] = filesize
73
         except:
80
         except:
74
             pass
81
             pass
75
 
82
 
80
 
87
 
81
         # extract and convert creation date
88
         # extract and convert creation date
82
         try:
89
         try:
83
-            date_str = result.xpath('./dd/span[@class="a"]/span')[0].attrib.get('title')
84
-            # Fri, 25 Mar 2016 16:29:01
85
-            date = datetime.strptime(date_str, '%a, %d %b %Y %H:%M:%S')
90
+            date_ts = result.xpath('./dd/span[2]')[0].attrib.get('title')
91
+            date = datetime.fromtimestamp(float(date_ts))
86
             params['publishedDate'] = date
92
             params['publishedDate'] = date
87
         except:
93
         except:
88
             pass
94
             pass

+ 8
- 10
searx/languages.py View File

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

+ 28
- 28
searx/settings.yml View File

189
     shortcut : et
189
     shortcut : et
190
     disabled : True
190
     disabled : True
191
 
191
 
192
-# api-key required: http://www.faroo.com/hp/api/api.html#key
193
-#  - name : faroo
194
-#    engine : faroo
195
-#    shortcut : fa
196
-#    api_key : 'apikey' # required!
192
+  - name : faroo
193
+    engine : faroo
194
+    shortcut : fa
195
+    disabled : True
197
 
196
 
198
   - name : 500px
197
   - name : 500px
199
     engine : www500px
198
     engine : www500px
247
     disabled: True
246
     disabled: True
248
 
247
 
249
   - name : gitlab
248
   - name : gitlab
250
-    engine : xpath
249
+    engine : json_engine
251
     paging : True
250
     paging : True
252
-    search_url : https://gitlab.com/search?page={pageno}&search={query}
253
-    url_xpath : //li[@class="project-row"]//a[@class="project"]/@href
254
-    title_xpath : //li[@class="project-row"]//span[contains(@class, "project-full-name")]
255
-    content_xpath : //li[@class="project-row"]//div[@class="description"]/p
251
+    search_url : https://gitlab.com/api/v4/projects?search={query}&page={pageno}
252
+    url_query : web_url
253
+    title_query : name_with_namespace
254
+    content_query : description
255
+    page_size : 20
256
     categories : it
256
     categories : it
257
     shortcut : gl
257
     shortcut : gl
258
-    timeout : 5.0
258
+    timeout : 10.0
259
     disabled : True
259
     disabled : True
260
 
260
 
261
   - name : github
261
   - name : github
326
     engine : xpath
326
     engine : xpath
327
     paging : True
327
     paging : True
328
     search_url : https://geektimes.ru/search/page{pageno}/?q={query}
328
     search_url : https://geektimes.ru/search/page{pageno}/?q={query}
329
-    url_xpath : //div[@class="search_results"]//a[@class="post__title_link"]/@href
330
-    title_xpath : //div[@class="search_results"]//a[@class="post__title_link"]
331
-    content_xpath : //div[@class="search_results"]//div[contains(@class, "content")]
329
+    url_xpath : //article[contains(@class, "post")]//a[@class="post__title_link"]/@href
330
+    title_xpath : //article[contains(@class, "post")]//a[@class="post__title_link"]
331
+    content_xpath : //article[contains(@class, "post")]//div[contains(@class, "post__text")]
332
     categories : it
332
     categories : it
333
     timeout : 4.0
333
     timeout : 4.0
334
     disabled : True
334
     disabled : True
338
     engine : xpath
338
     engine : xpath
339
     paging : True
339
     paging : True
340
     search_url : https://habrahabr.ru/search/page{pageno}/?q={query}
340
     search_url : https://habrahabr.ru/search/page{pageno}/?q={query}
341
-    url_xpath : //div[@class="search_results"]//a[contains(@class, "post__title_link")]/@href
342
-    title_xpath : //div[@class="search_results"]//a[contains(@class, "post__title_link")]
343
-    content_xpath : //div[@class="search_results"]//div[contains(@class, "content")]
341
+    url_xpath : //article[contains(@class, "post")]//a[@class="post__title_link"]/@href
342
+    title_xpath : //article[contains(@class, "post")]//a[@class="post__title_link"]
343
+    content_xpath : //article[contains(@class, "post")]//div[contains(@class, "post__text")]
344
     categories : it
344
     categories : it
345
     timeout : 4.0
345
     timeout : 4.0
346
     disabled : True
346
     disabled : True
556
     timeout : 6.0
556
     timeout : 6.0
557
     disabled : True
557
     disabled : True
558
 
558
 
559
+  - name : torrentz
560
+    engine : torrentz
561
+    shortcut : tor
562
+    url: https://torrentz2.eu/
563
+    timeout : 3.0
564
+
559
   - name : twitter
565
   - name : twitter
560
     engine : twitter
566
     engine : twitter
561
     shortcut : tw
567
     shortcut : tw
579
   - name : yahoo
585
   - name : yahoo
580
     engine : yahoo
586
     engine : yahoo
581
     shortcut : yh
587
     shortcut : yh
588
+    disabled : True
582
 
589
 
583
   - name : yandex
590
   - name : yandex
584
     engine : yandex
591
     engine : yandex
639
     engine: xpath
646
     engine: xpath
640
     shortcut: vo
647
     shortcut: vo
641
     categories: social media
648
     categories: social media
642
-    search_url : https://voat.co/search?q={query}
643
-    url_xpath : //p[contains(@class, "title")]/a/@href
644
-    title_xpath : //p[contains(@class, "title")]/a
645
-    content_xpath : //span[@class="domain"]
649
+    search_url : https://searchvoat.co/?t={query}
650
+    url_xpath : //div[@class="entry"]/p/a[@class="title"]/@href
651
+    title_xpath : //div[@class="entry"]/p/a[@class="title"]
652
+    content_xpath : //div[@class="entry"]/p/span[@class="domain"]
646
     timeout : 10.0
653
     timeout : 10.0
647
     disabled : True
654
     disabled : True
648
 
655
 
651
     shortcut : 1337x
658
     shortcut : 1337x
652
     disabled : True
659
     disabled : True
653
 
660
 
654
-#The blekko technology and team have joined IBM Watson! -> https://blekko.com/
655
-#  - name : blekko images
656
-#    engine : blekko_images
657
-#    locale : en-US
658
-#    shortcut : bli
659
-
660
 #  - name : yacy
661
 #  - name : yacy
661
 #    engine : yacy
662
 #    engine : yacy
662
 #    shortcut : ya
663
 #    shortcut : ya
676
     bg : Български (Bulgarian)
677
     bg : Български (Bulgarian)
677
     cs : Čeština (Czech)
678
     cs : Čeština (Czech)
678
     de : Deutsch (German)
679
     de : Deutsch (German)
679
-    de_DE : Deutsch (German_Germany)
680
     el_GR : Ελληνικά (Greek_Greece)
680
     el_GR : Ελληνικά (Greek_Greece)
681
     eo : Esperanto (Esperanto)
681
     eo : Esperanto (Esperanto)
682
     es : Español (Spanish)
682
     es : Español (Spanish)

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


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


+ 1
- 1
searx/static/themes/oscar/js/searx.min.js View File

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

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

19
 @import "cursor.less";
19
 @import "cursor.less";
20
 
20
 
21
 @import "code.less";
21
 @import "code.less";
22
+
23
+@import "preferences.less";

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

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

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

17
 @import "code.less";
17
 @import "code.less";
18
 
18
 
19
 @import "navbar.less";
19
 @import "navbar.less";
20
+
21
+@import "preferences.less";

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

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

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

1
-<div class="result {{ result.class }}">
2
-    <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>
3
-    {% if result.publishedDate %}<span class="published_date">{{ result.publishedDate }}</span>{% endif %}
4
-    <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>
5
-    {% 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 %}
6
-    <div dir="ltr">
7
-    {{ result.codelines|code_highlighter(result.code_language)|safe }}
8
-	</div>
9
-
10
-    <p class="url">{{ result.pretty_url }}&lrm;</p>
11
-</div>
1
+<div class="result {{ result.class }}">
2
+    <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>
3
+    {% if result.publishedDate %}<span class="published_date">{{ result.publishedDate }}</span>{% endif %}
4
+    <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>
5
+    {% 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 %}
6
+    <div dir="ltr">
7
+    {{ result.codelines|code_highlighter(result.code_language)|safe }}
8
+	</div>
9
+
10
+    <p class="url">{{ result.pretty_url }}&lrm;</p>
11
+</div>

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

1
-<div class="result {{ result.class }}">
2
-    <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>
3
-    <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>
4
-    {% if result.publishedDate %}<p class="published_date">{{ result.publishedDate }}</p>{% endif %}
5
-    <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>
6
-    {% 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 %}
7
-    
8
-    <div dir="ltr">
9
-    {{ result.codelines|code_highlighter(result.code_language)|safe }}
10
-	</div>
11
-</div>
1
+<div class="result {{ result.class }}">
2
+    <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>
3
+    <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>
4
+    {% if result.publishedDate %}<p class="published_date">{{ result.publishedDate }}</p>{% endif %}
5
+    <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>
6
+    {% 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 %}
7
+    
8
+    <div dir="ltr">
9
+    {{ result.codelines|code_highlighter(result.code_language)|safe }}
10
+	</div>
11
+</div>

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

1
-<!-- Draw glyphicon icon from bootstrap-theme -->
2
-{% macro icon(action) -%}
3
-    <span class="glyphicon glyphicon-{{ action }}"></span>
4
-{%- endmacro %}
5
-
6
-<!-- Draw favicon -->
7
-<!-- TODO: using url_for methode -->
8
-{% macro draw_favicon(favicon) -%}
9
-    <img width="32" height="32" class="favicon" src="static/themes/oscar/img/icons/{{ favicon }}.png" alt="{{ favicon }}" />
10
-{%- endmacro %}
11
-
12
-{%- macro result_link(url, title, classes='') -%}
13
-<a href="{{ url }}" {% if classes %}class="{{ classes }}" {% endif %}{% if results_on_new_tab %}target="_blank" rel="noopener noreferrer"{% else %}rel="noreferrer"{% endif %}>{{ title }}</a>
14
-{%- endmacro -%}
15
-
16
-<!-- Draw result header -->
17
-{% macro result_header(result, favicons) -%}
18
-<h4 class="result_header">{% if result.engine~".png" in favicons %}{{ draw_favicon(result.engine) }} {% endif %}{{ result_link(result.url, result.title|safe) }}</h4>
19
-{%- endmacro %}
20
-
21
-<!-- Draw result sub header -->
22
-{% macro result_sub_header(result) -%}
23
-    {% if result.publishedDate %}<time class="text-muted" datetime="{{ result.pubdate }}" >{{ result.publishedDate }}</time>{% endif %}
24
-    {% if result.magnetlink %}<small> &bull; {{ result_link(result.magnetlink, icon('magnet') + _('magnet link'), "magnetlink") }}</small>{% endif %}
25
-    {% if result.torrentfile %}<small> &bull; {{ result_link(result.torrentfile, icon('download-alt') + _('torrent file'), "torrentfile") }}</small>{% endif %}
26
-{%- endmacro %}
27
-
28
-<!-- Draw result footer -->
29
-{% macro result_footer(result) -%}
30
-    <div class="clearfix"></div>
31
-    <div class="pull-right">
32
-    {% for engine in result.engines %}
33
-        <span class="label label-default">{{ engine }}</span>
34
-    {% endfor %}
35
-    <small>{{ result_link("https://web.archive.org/web/" + result.url, icon('link') + _('cached'), "text-info") }}</small>
36
-    {% if proxify %}
37
-    <small>{{ result_link(proxify(result.url), icon('sort') + _('proxied'), "text-info") }}</small>
38
-    {% endif %}
39
-</div>
40
-<div class="external-link">{{ result.pretty_url }}</div>
41
-{%- endmacro %}
42
-
43
-<!-- Draw result footer -->
44
-{% macro result_footer_rtl(result) -%}
45
-    <div class="clearfix"></div>
46
-    {% for engine in result.engines %}
47
-        <span class="label label-default">{{ engine }}</span>
48
-    {% endfor %}
49
-    <small>{{ result_link("https://web.archive.org/web/" + result.url, icon('link') + _('cached'), "text-info") }}</small>
50
-    {% if proxify %}
51
-    <small>{{ result_link(proxify(result.url), icon('sort') + _('proxied'), "text-info") }}</small>
52
-    {% endif %}
53
-    <div class="external-link">{{ result.pretty_url }}</div>
54
-{%- endmacro %}
55
-
56
-{% macro preferences_item_header(info, label, rtl) -%}
57
-    {% if rtl %}
58
-    <div class="row form-group">
59
-        <label class="col-sm-3 col-md-2 pull-right">{{ label }}</label>
60
-        <span class="col-sm-5 col-md-6 help-block pull-left">{{ info }}</span>
61
-        <div class="col-sm-4 col-md-4">
62
-    {% else %}
63
-    <div class="row form-group">
64
-        <label class="col-sm-3 col-md-2">{{ label }}</label>
65
-        <div class="col-sm-4 col-md-4">
66
-    {% endif %}
67
-{%- endmacro %}
68
-
69
-{% macro preferences_item_footer(info, label, rtl) -%}
70
-    {% if rtl %}
71
-        </div>
72
-    </div>
73
-    {% else %}
74
-        </div>
75
-        <span class="col-sm-5 col-md-6 help-block">{{ info }}</span>
76
-    </div>
77
-    {% endif %}
78
-{%- endmacro %}
79
-
80
-{% macro checkbox_toggle(id, blocked) -%}
81
-    <div class="onoffswitch">
82
-        <input type="checkbox" id="{{ id }}" name="{{ id }}"{% if blocked %} checked="checked"{% endif %} class="onoffswitch-checkbox">
83
-        <label class="onoffswitch-label" for="{{ id }}">
84
-            <span class="onoffswitch-inner"></span>
85
-            <span class="onoffswitch-switch"></span>
86
-        </label>
87
-    </div>
88
-{%- endmacro %}
1
+<!-- Draw glyphicon icon from bootstrap-theme -->
2
+{% macro icon(action) -%}
3
+    <span class="glyphicon glyphicon-{{ action }}"></span>
4
+{%- endmacro %}
5
+
6
+<!-- Draw favicon -->
7
+{% macro draw_favicon(favicon) -%}
8
+    <img width="32" height="32" class="favicon" src="{{ url_for('static', filename='themes/oscar/img/icons/' + favicon + '.png') }}" alt="{{ favicon }}" />
9
+{%- endmacro %}
10
+
11
+{%- macro result_link(url, title, classes='') -%}
12
+<a href="{{ url }}" {% if classes %}class="{{ classes }}" {% endif %}{% if results_on_new_tab %}target="_blank" rel="noopener noreferrer"{% else %}rel="noreferrer"{% endif %}>{{ title }}</a>
13
+{%- endmacro -%}
14
+
15
+<!-- Draw result header -->
16
+{% macro result_header(result, favicons) -%}
17
+<h4 class="result_header">{% if result.engine~".png" in favicons %}{{ draw_favicon(result.engine) }} {% endif %}{{ result_link(result.url, result.title|safe) }}</h4>
18
+{%- endmacro %}
19
+
20
+<!-- Draw result sub header -->
21
+{% macro result_sub_header(result) -%}
22
+    {% if result.publishedDate %}<time class="text-muted" datetime="{{ result.pubdate }}" >{{ result.publishedDate }}</time>{% endif %}
23
+    {% if result.magnetlink %}<small> &bull; {{ result_link(result.magnetlink, icon('magnet') + _('magnet link'), "magnetlink") }}</small>{% endif %}
24
+    {% if result.torrentfile %}<small> &bull; {{ result_link(result.torrentfile, icon('download-alt') + _('torrent file'), "torrentfile") }}</small>{% endif %}
25
+{%- endmacro %}
26
+
27
+<!-- Draw result footer -->
28
+{% macro result_footer(result) -%}
29
+    <div class="clearfix"></div>
30
+    <div class="pull-right">
31
+    {% for engine in result.engines %}
32
+        <span class="label label-default">{{ engine }}</span>
33
+    {% endfor %}
34
+    <small>{{ result_link("https://web.archive.org/web/" + result.url, icon('link') + _('cached'), "text-info") }}</small>
35
+    {% if proxify %}
36
+    <small>{{ result_link(proxify(result.url), icon('sort') + _('proxied'), "text-info") }}</small>
37
+    {% endif %}
38
+</div>
39
+<div class="external-link">{{ result.pretty_url }}</div>
40
+{%- endmacro %}
41
+
42
+<!-- Draw result footer -->
43
+{% macro result_footer_rtl(result) -%}
44
+    <div class="clearfix"></div>
45
+    {% for engine in result.engines %}
46
+        <span class="label label-default">{{ engine }}</span>
47
+    {% endfor %}
48
+    <small>{{ result_link("https://web.archive.org/web/" + result.url, icon('link') + _('cached'), "text-info") }}</small>
49
+    {% if proxify %}
50
+    <small>{{ result_link(proxify(result.url), icon('sort') + _('proxied'), "text-info") }}</small>
51
+    {% endif %}
52
+    <div class="external-link">{{ result.pretty_url }}</div>
53
+{%- endmacro %}
54
+
55
+{% macro preferences_item_header(info, label, rtl) -%}
56
+    {% if rtl %}
57
+    <div class="row form-group">
58
+        <label class="col-sm-3 col-md-2 pull-right">{{ label }}</label>
59
+        <span class="col-sm-5 col-md-6 help-block pull-left">{{ info }}</span>
60
+        <div class="col-sm-4 col-md-4">
61
+    {% else %}
62
+    <div class="row form-group">
63
+        <label class="col-sm-3 col-md-2">{{ label }}</label>
64
+        <div class="col-sm-4 col-md-4">
65
+    {% endif %}
66
+{%- endmacro %}
67
+
68
+{% macro preferences_item_footer(info, label, rtl) -%}
69
+    {% if rtl %}
70
+        </div>
71
+    </div>
72
+    {% else %}
73
+        </div>
74
+        <span class="col-sm-5 col-md-6 help-block">{{ info }}</span>
75
+    </div>
76
+    {% endif %}
77
+{%- endmacro %}
78
+
79
+{% macro checkbox_toggle(id, blocked) -%}
80
+    <div class="onoffswitch">
81
+        <input type="checkbox" id="{{ id }}" name="{{ id }}"{% if blocked %} checked="checked"{% endif %} class="onoffswitch-checkbox">
82
+        <label class="onoffswitch-label" for="{{ id }}">
83
+            <span class="onoffswitch-inner"></span>
84
+            <span class="onoffswitch-switch"></span>
85
+        </label>
86
+    </div>
87
+{%- endmacro %}
88
+
89
+{% macro support_toggle(supports) -%}
90
+    {% if supports %}
91
+    <span class="label label-success">
92
+        {{ _("supported") }}
93
+    </span>
94
+    {% else %}
95
+    <span class="label label-danger">
96
+        {{ _("not supported") }}
97
+    </span>
98
+    {% endif %}
99
+{%- endmacro %}

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

1
-{% from 'oscar/macros.html' import preferences_item_header, preferences_item_header_rtl, preferences_item_footer, preferences_item_footer_rtl, checkbox_toggle %}
1
+{% from 'oscar/macros.html' import preferences_item_header, preferences_item_header_rtl, preferences_item_footer, preferences_item_footer_rtl, checkbox_toggle, support_toggle %}
2
 {% extends "oscar/base.html" %}
2
 {% extends "oscar/base.html" %}
3
 {% block title %}{{ _('preferences') }} - {% endblock %}
3
 {% block title %}{{ _('preferences') }} - {% endblock %}
4
 {% block content %}
4
 {% block content %}
5
+
5
 <div>
6
 <div>
6
 
7
 
7
     <h1>{{ _('Preferences') }}</h1>
8
     <h1>{{ _('Preferences') }}</h1>
148
 				    <th>{{ _("Allow") }}</th>
149
 				    <th>{{ _("Allow") }}</th>
149
 				    <th>{{ _("Engine name") }}</th>
150
 				    <th>{{ _("Engine name") }}</th>
150
 				    <th>{{ _("Shortcut") }}</th>
151
 				    <th>{{ _("Shortcut") }}</th>
151
-				    <th>{{ _("Supports selected language") }}</th>
152
+				    <th>{{ _("Selected language") }}</th>
152
 				    <th>{{ _("SafeSearch") }}</th>
153
 				    <th>{{ _("SafeSearch") }}</th>
153
 				    <th>{{ _("Time range") }}</th>
154
 				    <th>{{ _("Time range") }}</th>
154
 				    <th>{{ _("Avg. time") }}</th>
155
 				    <th>{{ _("Avg. time") }}</th>
156
                                     {% else %}
157
                                     {% else %}
157
 				    <th>{{ _("Max time") }}</th>
158
 				    <th>{{ _("Max time") }}</th>
158
 				    <th>{{ _("Avg. time") }}</th>
159
 				    <th>{{ _("Avg. time") }}</th>
160
+				    <th>{{ _("Time range") }}</th>
159
 				    <th>{{ _("SafeSearch") }}</th>
161
 				    <th>{{ _("SafeSearch") }}</th>
160
-				    <th>{{ _("Supports selected language") }}</th>
162
+				    <th>{{ _("Selected language") }}</th>
161
 				    <th>{{ _("Shortcut") }}</th>
163
 				    <th>{{ _("Shortcut") }}</th>
162
 				    <th>{{ _("Engine name") }}</th>
164
 				    <th>{{ _("Engine name") }}</th>
163
 				    <th>{{ _("Allow") }}</th>
165
 				    <th>{{ _("Allow") }}</th>
172
                                     </td>
174
                                     </td>
173
                                     <th>{{ search_engine.name }}</th>
175
                                     <th>{{ search_engine.name }}</th>
174
 				    <td class="name">{{ shortcuts[search_engine.name] }}</td>
176
 				    <td class="name">{{ shortcuts[search_engine.name] }}</td>
175
-				    <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>
176
-				    <td><input type="checkbox" {{ "checked" if search_engine.safesearch==True else ""}} readonly="readonly" disabled="disabled"></td>
177
-				    <td><input type="checkbox" {{ "checked" if search_engine.time_range_support==True else ""}} readonly="readonly" disabled="disabled"></td>
178
-				    <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>
179
-				    <td class="{{ 'danger' if stats[search_engine.name]['warn_timeout'] else '' }}">{{ search_engine.timeout }}</td>
180
-                                    {% else %}
181
-				    <td class="{{ 'danger' if stats[search_engine.name]['warn_timeout'] else '' }}">{{ search_engine.timeout }}</td>
182
-				    <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>
183
-				    <td><input type="checkbox" {{ "checked" if search_engine.safesearch==True else ""}} readonly="readonly" disabled="disabled"></td>
184
-				    <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>
185
-				    <td>{{ shortcuts[search_engine.name] }}</td>
177
+					<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>
178
+					<td>{{ support_toggle(search_engine.safesearch==True) }}</td>
179
+					<td>{{ support_toggle(search_engine.time_range_support==True) }}</td>
180
+					<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>
181
+					<td class="{{ 'danger' if stats[search_engine.name]['warn_timeout'] else '' }}">{{ search_engine.timeout }}</td>
182
+									{% else %}
183
+					<td class="{{ 'danger' if stats[search_engine.name]['warn_timeout'] else '' }}">{{ search_engine.timeout }}</td>
184
+					<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>
185
+					<td>{{ support_toggle(search_engine.time_range_support==True) }}</td>
186
+					<td>{{ support_toggle(search_engine.safesearch==True) }}</td>
187
+					<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>
188
+					<td>{{ shortcuts[search_engine.name] }}</td>
186
                                     <th>{{ search_engine.name }}</th>
189
                                     <th>{{ search_engine.name }}</th>
187
                                     <td class="onoff-checkbox">
190
                                     <td class="onoff-checkbox">
188
                                         {{ checkbox_toggle('engine_' + search_engine.name|replace(' ', '_') + '__' + categ|replace(' ', '_'), (search_engine.name, categ) in disabled_engines) }}
191
                                         {{ checkbox_toggle('engine_' + search_engine.name|replace(' ', '_') + '__' + categ|replace(' ', '_'), (search_engine.name, categ) in disabled_engines) }}

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

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

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


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

1
-# Translations template for PROJECT.
2
-# Copyright (C) 2016 ORGANIZATION
3
-# This file is distributed under the same license as the PROJECT project.
4
-# 
5
-# Translators:
6
-# Bamstam, 2016-2017
7
-# Benjamin Richter <benjamin@hacktherack.de>, 2015
8
-# cy8aer <cybaer42@web.de>, 2016-2017
9
-msgid ""
10
-msgstr ""
11
-"Project-Id-Version: searx\n"
12
-"Report-Msgid-Bugs-To: EMAIL@ADDRESS\n"
13
-"POT-Creation-Date: 2016-12-29 10:42+0100\n"
14
-"PO-Revision-Date: 2017-05-19 20:17+0000\n"
15
-"Last-Translator: cy8aer <cybaer42@web.de>\n"
16
-"Language-Team: German (Germany) (http://www.transifex.com/asciimoo/searx/language/de_DE/)\n"
17
-"MIME-Version: 1.0\n"
18
-"Content-Type: text/plain; charset=UTF-8\n"
19
-"Content-Transfer-Encoding: 8bit\n"
20
-"Generated-By: Babel 2.3.4\n"
21
-"Language: de_DE\n"
22
-"Plural-Forms: nplurals=2; plural=(n != 1);\n"
23
-
24
-#: searx/webapp.py:123
25
-msgid "files"
26
-msgstr "Dateien"
27
-
28
-#: searx/webapp.py:124
29
-msgid "general"
30
-msgstr "Allgemein"
31
-
32
-#: searx/webapp.py:125
33
-msgid "music"
34
-msgstr "Musik"
35
-
36
-#: searx/webapp.py:126
37
-msgid "social media"
38
-msgstr "Soziale Medien"
39
-
40
-#: searx/webapp.py:127
41
-msgid "images"
42
-msgstr "Fotos"
43
-
44
-#: searx/webapp.py:128
45
-msgid "videos"
46
-msgstr "Videos"
47
-
48
-#: searx/webapp.py:129
49
-msgid "it"
50
-msgstr "IT"
51
-
52
-#: searx/webapp.py:130
53
-msgid "news"
54
-msgstr "Nachrichten"
55
-
56
-#: searx/webapp.py:131
57
-msgid "map"
58
-msgstr "Karten"
59
-
60
-#: searx/webapp.py:132
61
-msgid "science"
62
-msgstr "Wissenschaft"
63
-
64
-#: searx/webapp.py:384 searx/webapp.py:594
65
-msgid "Invalid settings, please edit your preferences"
66
-msgstr "Ungültige Auswahl, bitte überprüfen Sie die Einstellungen"
67
-
68
-#: searx/webapp.py:425
69
-msgid "search error"
70
-msgstr "Fehler bei der Suche"
71
-
72
-#: searx/webapp.py:467
73
-msgid "{minutes} minute(s) ago"
74
-msgstr "vor {minutes} Minute(n)"
75
-
76
-#: searx/webapp.py:469
77
-msgid "{hours} hour(s), {minutes} minute(s) ago"
78
-msgstr "vor {hours} Stunde(n). {minutes} Minute(n)"
79
-
80
-#: searx/answerers/random/answerer.py:48
81
-msgid "Random value generator"
82
-msgstr "Zufallswertgenerator"
83
-
84
-#: searx/answerers/random/answerer.py:49
85
-msgid "Generate different random values"
86
-msgstr "Zufallswerte generieren"
87
-
88
-#: searx/answerers/statistics/answerer.py:49
89
-msgid "Statistics functions"
90
-msgstr "Statistik-Funktionen"
91
-
92
-#: searx/answerers/statistics/answerer.py:50
93
-msgid "Compute {functions} of the arguments"
94
-msgstr "{functions} der Argumente berechnen"
95
-
96
-#: searx/engines/__init__.py:192
97
-msgid "Engine time (sec)"
98
-msgstr "Zeitbedarf (s)"
99
-
100
-#: searx/engines/__init__.py:196
101
-msgid "Page loads (sec)"
102
-msgstr "Ladezeit (s)"
103
-
104
-#: searx/engines/__init__.py:200 searx/templates/oscar/results.html:88
105
-msgid "Number of results"
106
-msgstr "Anzahl Ergebnisse"
107
-
108
-#: searx/engines/__init__.py:204
109
-msgid "Scores"
110
-msgstr "Punktwerte"
111
-
112
-#: searx/engines/__init__.py:208
113
-msgid "Scores per result"
114
-msgstr "Punktwerte pro Ergebnis"
115
-
116
-#: searx/engines/__init__.py:212
117
-msgid "Errors"
118
-msgstr "Fehler"
119
-
120
-#: searx/engines/pdbe.py:87
121
-msgid "{title}&nbsp;(OBSOLETE)"
122
-msgstr "{title}&nbsp;(OBSOLET)"
123
-
124
-#: searx/engines/pdbe.py:91
125
-msgid "This entry has been superseded by"
126
-msgstr "Dieser Eintrag wurde ersetzt durch"
127
-
128
-#: searx/plugins/doai_rewrite.py:7
129
-msgid "DOAI rewrite"
130
-msgstr "DOAI-Umgehung"
131
-
132
-#: searx/plugins/doai_rewrite.py:8
133
-msgid ""
134
-"Avoid paywalls by redirecting to open-access versions of publications when "
135
-"available"
136
-msgstr "Paywalls umgehen, indem wenn möglich auf Open-Access-Versionen von Publikationen umgeleitet wird"
137
-
138
-#: searx/plugins/https_rewrite.py:29
139
-msgid "Rewrite HTTP links to HTTPS if possible"
140
-msgstr "Umschreiben von HTTP-Links nach HTTPS, wenn möglich"
141
-
142
-#: searx/plugins/infinite_scroll.py:3
143
-msgid "Infinite scroll"
144
-msgstr "Unbegrenztes Scrollen"
145
-
146
-#: searx/plugins/infinite_scroll.py:4
147
-msgid "Automatically load next page when scrolling to bottom of current page"
148
-msgstr "Nächste Seite automatisch laden, wenn zum Seitenende gescrollt wird"
149
-
150
-#: searx/plugins/open_results_on_new_tab.py:18
151
-#: searx/templates/oscar/preferences.html:113
152
-msgid "Open result links on new browser tabs"
153
-msgstr "Öffne Links in einem neuen Browser-Tab"
154
-
155
-#: searx/plugins/open_results_on_new_tab.py:19
156
-msgid ""
157
-"Results are opened in the same window by default. This plugin overwrites the"
158
-" default behaviour to open links on new tabs/windows. (JavaScript required)"
159
-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)."
160
-
161
-#: searx/plugins/search_on_category_select.py:18
162
-msgid "Search on category select"
163
-msgstr "Suchen nach Kategorie"
164
-
165
-#: searx/plugins/search_on_category_select.py:19
166
-msgid ""
167
-"Perform search immediately if a category selected. Disable to select "
168
-"multiple categories. (JavaScript required)"
169
-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)."
170
-
171
-#: searx/plugins/self_info.py:20
172
-msgid ""
173
-"Displays your IP if the query is \"ip\" and your user agent if the query "
174
-"contains \"user agent\"."
175
-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."
176
-
177
-#: searx/plugins/tracker_url_remover.py:26
178
-msgid "Tracker URL remover"
179
-msgstr "Tracking-URLs bereinigen"
180
-
181
-#: searx/plugins/tracker_url_remover.py:27
182
-msgid "Remove trackers arguments from the returned URL"
183
-msgstr "Tracker-Argumente der erhaltenen URL entfernen"
184
-
185
-#: searx/plugins/vim_hotkeys.py:3
186
-msgid "Vim-like hotkeys"
187
-msgstr "Vim-ähnliche Hotkeys"
188
-
189
-#: searx/plugins/vim_hotkeys.py:4
190
-msgid ""
191
-"Navigate search results with Vim-like hotkeys (JavaScript required). Press "
192
-"\"h\" key on main or result page to get help."
193
-msgstr "Durch Suchergebnisse navigieren mit Vim-ähnlichen Hotkeys (benötigt JavaScript). \"h\" drücken auf der Hauptseite oder der Ergebnisseite, um Hilfe zu erhalten."
194
-
195
-#: searx/templates/courgette/404.html:4 searx/templates/legacy/404.html:4
196
-#: searx/templates/oscar/404.html:4 searx/templates/pix-art/404.html:4
197
-msgid "Page not found"
198
-msgstr "Seite nicht gefunden"
199
-
200
-#: searx/templates/courgette/404.html:6 searx/templates/legacy/404.html:6
201
-#: searx/templates/oscar/404.html:6 searx/templates/pix-art/404.html:6
202
-#, python-format
203
-msgid "Go to %(search_page)s."
204
-msgstr "Gehe zu %(search_page)s."
205
-
206
-#: searx/templates/courgette/404.html:6 searx/templates/legacy/404.html:6
207
-#: searx/templates/oscar/404.html:6 searx/templates/pix-art/404.html:6
208
-msgid "search page"
209
-msgstr "Seite durchsuchen"
210
-
211
-#: searx/templates/courgette/index.html:9
212
-#: searx/templates/courgette/index.html:13
213
-#: searx/templates/courgette/results.html:5
214
-#: searx/templates/legacy/index.html:8 searx/templates/legacy/index.html:12
215
-#: searx/templates/oscar/navbar.html:12
216
-#: searx/templates/oscar/preferences.html:3
217
-#: searx/templates/pix-art/index.html:8
218
-msgid "preferences"
219
-msgstr "Einstellungen"
220
-
221
-#: searx/templates/courgette/index.html:11
222
-#: searx/templates/legacy/index.html:10 searx/templates/oscar/about.html:2
223
-#: searx/templates/oscar/navbar.html:11 searx/templates/pix-art/index.html:7
224
-msgid "about"
225
-msgstr "Über uns"
226
-
227
-#: searx/templates/courgette/preferences.html:5
228
-#: searx/templates/legacy/preferences.html:5
229
-#: searx/templates/oscar/preferences.html:7
230
-#: searx/templates/pix-art/preferences.html:5
231
-msgid "Preferences"
232
-msgstr "Einstellungen"
233
-
234
-#: searx/templates/courgette/preferences.html:9
235
-#: searx/templates/legacy/preferences.html:9
236
-#: searx/templates/oscar/preferences.html:32
237
-#: searx/templates/oscar/preferences.html:34
238
-msgid "Default categories"
239
-msgstr "Standardkategorien"
240
-
241
-#: searx/templates/courgette/preferences.html:13
242
-#: searx/templates/legacy/preferences.html:14
243
-#: searx/templates/oscar/preferences.html:40
244
-#: searx/templates/pix-art/preferences.html:9
245
-msgid "Search language"
246
-msgstr "Suchsprache"
247
-
248
-#: searx/templates/courgette/preferences.html:16
249
-#: searx/templates/legacy/preferences.html:17
250
-#: searx/templates/oscar/languages.html:6
251
-#: searx/templates/pix-art/preferences.html:12
252
-msgid "Default language"
253
-msgstr "Standardsprache"
254
-
255
-#: searx/templates/courgette/preferences.html:24
256
-#: searx/templates/legacy/preferences.html:25
257
-#: searx/templates/oscar/preferences.html:46
258
-#: searx/templates/pix-art/preferences.html:20
259
-msgid "Interface language"
260
-msgstr "Sprache der Benutzeroberfläche"
261
-
262
-#: searx/templates/courgette/preferences.html:34
263
-#: searx/templates/legacy/preferences.html:35
264
-#: searx/templates/oscar/preferences.html:56
265
-msgid "Autocomplete"
266
-msgstr "Autovervollständigen"
267
-
268
-#: searx/templates/courgette/preferences.html:45
269
-#: searx/templates/legacy/preferences.html:46
270
-#: searx/templates/oscar/preferences.html:67
271
-msgid "Image proxy"
272
-msgstr "Proxy-Server für Bilder"
273
-
274
-#: searx/templates/courgette/preferences.html:48
275
-#: searx/templates/legacy/preferences.html:49
276
-#: searx/templates/oscar/preferences.html:71
277
-msgid "Enabled"
278
-msgstr "Aktiviert"
279
-
280
-#: searx/templates/courgette/preferences.html:49
281
-#: searx/templates/legacy/preferences.html:50
282
-#: searx/templates/oscar/preferences.html:72
283
-msgid "Disabled"
284
-msgstr "Deaktiviert"
285
-
286
-#: searx/templates/courgette/preferences.html:54
287
-#: searx/templates/legacy/preferences.html:55
288
-#: searx/templates/oscar/preferences.html:76
289
-#: searx/templates/pix-art/preferences.html:30
290
-msgid "Method"
291
-msgstr "Methode"
292
-
293
-#: searx/templates/courgette/preferences.html:63
294
-#: searx/templates/legacy/preferences.html:64
295
-#: searx/templates/oscar/preferences.html:85
296
-#: searx/templates/oscar/preferences.html:152
297
-#: searx/templates/oscar/preferences.html:159
298
-msgid "SafeSearch"
299
-msgstr "SafeSearch"
300
-
301
-#: searx/templates/courgette/preferences.html:66
302
-#: searx/templates/legacy/preferences.html:67
303
-#: searx/templates/oscar/preferences.html:89
304
-msgid "Strict"
305
-msgstr "Streng"
306
-
307
-#: searx/templates/courgette/preferences.html:67
308
-#: searx/templates/legacy/preferences.html:68
309
-#: searx/templates/oscar/preferences.html:90
310
-msgid "Moderate"
311
-msgstr "Moderat"
312
-
313
-#: searx/templates/courgette/preferences.html:68
314
-#: searx/templates/legacy/preferences.html:69
315
-#: searx/templates/oscar/preferences.html:91
316
-msgid "None"
317
-msgstr "Keine"
318
-
319
-#: searx/templates/courgette/preferences.html:73
320
-#: searx/templates/legacy/preferences.html:74
321
-#: searx/templates/oscar/preferences.html:95
322
-#: searx/templates/pix-art/preferences.html:39
323
-msgid "Themes"
324
-msgstr "Oberflächen"
325
-
326
-#: searx/templates/courgette/preferences.html:83
327
-msgid "Color"
328
-msgstr "Farbe"
329
-
330
-#: searx/templates/courgette/preferences.html:86
331
-msgid "Blue (default)"
332
-msgstr "Blau (Standard)"
333
-
334
-#: searx/templates/courgette/preferences.html:87
335
-msgid "Violet"
336
-msgstr "Violett"
337
-
338
-#: searx/templates/courgette/preferences.html:88
339
-msgid "Green"
340
-msgstr "Grün"
341
-
342
-#: searx/templates/courgette/preferences.html:89
343
-msgid "Cyan"
344
-msgstr "Türkis"
345
-
346
-#: searx/templates/courgette/preferences.html:90
347
-msgid "Orange"
348
-msgstr "Orange"
349
-
350
-#: searx/templates/courgette/preferences.html:91
351
-msgid "Red"
352
-msgstr "Rot"
353
-
354
-#: searx/templates/courgette/preferences.html:96
355
-#: searx/templates/legacy/preferences.html:93
356
-#: searx/templates/pix-art/preferences.html:49
357
-msgid "Currently used search engines"
358
-msgstr "Momentan genutzte Suchmaschinen"
359
-
360
-#: searx/templates/courgette/preferences.html:100
361
-#: searx/templates/legacy/preferences.html:97
362
-#: searx/templates/oscar/preferences.html:149
363
-#: searx/templates/oscar/preferences.html:162
364
-#: searx/templates/pix-art/preferences.html:53
365
-msgid "Engine name"
366
-msgstr "Suchmaschinen-Name"
367
-
368
-#: searx/templates/courgette/preferences.html:101
369
-#: searx/templates/legacy/preferences.html:98
370
-msgid "Category"
371
-msgstr "Kategorie"
372
-
373
-#: searx/templates/courgette/preferences.html:102
374
-#: searx/templates/courgette/preferences.html:113
375
-#: searx/templates/legacy/preferences.html:99
376
-#: searx/templates/legacy/preferences.html:110
377
-#: searx/templates/oscar/preferences.html:148
378
-#: searx/templates/oscar/preferences.html:163
379
-#: searx/templates/pix-art/preferences.html:54
380
-#: searx/templates/pix-art/preferences.html:64
381
-msgid "Allow"
382
-msgstr "Zulassen"
383
-
384
-#: searx/templates/courgette/preferences.html:102
385
-#: searx/templates/courgette/preferences.html:114
386
-#: searx/templates/legacy/preferences.html:99
387
-#: searx/templates/legacy/preferences.html:111
388
-#: searx/templates/pix-art/preferences.html:54
389
-#: searx/templates/pix-art/preferences.html:65
390
-msgid "Block"
391
-msgstr "Blockieren"
392
-
393
-#: searx/templates/courgette/preferences.html:122
394
-#: searx/templates/legacy/preferences.html:119
395
-#: searx/templates/oscar/preferences.html:282
396
-#: searx/templates/pix-art/preferences.html:73
397
-msgid ""
398
-"These settings are stored in your cookies, this allows us not to store this "
399
-"data about you."
400
-msgstr "Diese Einstellungen werden in Ihren Cookies gespeichert, deshalb müssen wir diese persönlichen Daten nicht bei uns speichern."
401
-
402
-#: searx/templates/courgette/preferences.html:124
403
-#: searx/templates/legacy/preferences.html:121
404
-#: searx/templates/oscar/preferences.html:284
405
-#: searx/templates/pix-art/preferences.html:75
406
-msgid ""
407
-"These cookies serve your sole convenience, we don't use these cookies to "
408
-"track you."
409
-msgstr "Diese Cookies ermöglichen lediglich eine komfortablere Nutzung, wir verwenden diese Cookies nicht, um Sie zu tracken."
410
-
411
-#: searx/templates/courgette/preferences.html:127
412
-#: searx/templates/legacy/preferences.html:124
413
-#: searx/templates/oscar/preferences.html:287
414
-#: searx/templates/pix-art/preferences.html:78
415
-msgid "save"
416
-msgstr "speichern"
417
-
418
-#: searx/templates/courgette/preferences.html:128
419
-#: searx/templates/legacy/preferences.html:125
420
-#: searx/templates/oscar/preferences.html:289
421
-msgid "Reset defaults"
422
-msgstr "Voreinstellungen wiederherstellen"
423
-
424
-#: searx/templates/courgette/preferences.html:129
425
-#: searx/templates/legacy/preferences.html:126
426
-#: searx/templates/oscar/preferences.html:288
427
-#: searx/templates/pix-art/preferences.html:79
428
-msgid "back"
429
-msgstr "zurück"
430
-
431
-#: searx/templates/courgette/results.html:12
432
-#: searx/templates/legacy/results.html:13
433
-#: searx/templates/oscar/results.html:124
434
-msgid "Search URL"
435
-msgstr "Such-URL"
436
-
437
-#: searx/templates/courgette/results.html:16
438
-#: searx/templates/legacy/results.html:17
439
-#: searx/templates/oscar/results.html:129
440
-msgid "Download results"
441
-msgstr "Suchergebnisse herunterladen"
442
-
443
-#: searx/templates/courgette/results.html:34
444
-#: searx/templates/legacy/results.html:35
445
-msgid "Answers"
446
-msgstr "Antworten"
447
-
448
-#: searx/templates/courgette/results.html:42
449
-#: searx/templates/legacy/results.html:43
450
-#: searx/templates/oscar/results.html:104
451
-msgid "Suggestions"
452
-msgstr "Vorschläge"
453
-
454
-#: searx/templates/courgette/results.html:70
455
-#: searx/templates/legacy/results.html:81
456
-#: searx/templates/oscar/results.html:53 searx/templates/oscar/results.html:66
457
-msgid "previous page"
458
-msgstr "vorherige Seite"
459
-
460
-#: searx/templates/courgette/results.html:81
461
-#: searx/templates/legacy/results.html:92
462
-#: searx/templates/oscar/results.html:45 searx/templates/oscar/results.html:75
463
-msgid "next page"
464
-msgstr "nächste Seite"
465
-
466
-#: searx/templates/courgette/search.html:3
467
-#: searx/templates/legacy/search.html:3 searx/templates/oscar/search.html:4
468
-#: searx/templates/oscar/search_full.html:9
469
-#: searx/templates/pix-art/search.html:3
470
-msgid "Search for..."
471
-msgstr "Suchen nach ..."
472
-
473
-#: searx/templates/courgette/stats.html:4 searx/templates/legacy/stats.html:4
474
-#: searx/templates/oscar/stats.html:5 searx/templates/pix-art/stats.html:4
475
-msgid "Engine stats"
476
-msgstr "Suchmaschinen-Statistiken"
477
-
478
-#: searx/templates/courgette/result_templates/images.html:4
479
-#: searx/templates/legacy/result_templates/images.html:4
480
-#: searx/templates/pix-art/result_templates/images.html:4
481
-msgid "original context"
482
-msgstr "Ursprünglicher Kontext"
483
-
484
-#: searx/templates/courgette/result_templates/torrent.html:7
485
-#: searx/templates/legacy/result_templates/torrent.html:11
486
-#: searx/templates/oscar/result_templates/torrent.html:6
487
-msgid "Seeder"
488
-msgstr "Seeder"
489
-
490
-#: searx/templates/courgette/result_templates/torrent.html:7
491
-#: searx/templates/legacy/result_templates/torrent.html:11
492
-#: searx/templates/oscar/result_templates/torrent.html:6
493
-msgid "Leecher"
494
-msgstr "Leecher"
495
-
496
-#: searx/templates/courgette/result_templates/torrent.html:9
497
-#: searx/templates/legacy/result_templates/torrent.html:9
498
-#: searx/templates/oscar/macros.html:24
499
-msgid "magnet link"
500
-msgstr "Magnet-Link"
501
-
502
-#: searx/templates/courgette/result_templates/torrent.html:10
503
-#: searx/templates/legacy/result_templates/torrent.html:10
504
-#: searx/templates/oscar/macros.html:25
505
-msgid "torrent file"
506
-msgstr "Torrent-Datei"
507
-
508
-#: searx/templates/legacy/categories.html:8
509
-msgid "Click on the magnifier to perform search"
510
-msgstr "Klicken Sie auf das Vergrößerungsglas, um die Suche zu starten"
511
-
512
-#: searx/templates/legacy/preferences.html:84
513
-#: searx/templates/oscar/preferences.html:112
514
-msgid "Results on new tabs"
515
-msgstr "Ergebnisse in neuen Tabs"
516
-
517
-#: searx/templates/legacy/preferences.html:87
518
-#: searx/templates/oscar/preferences.html:116
519
-msgid "On"
520
-msgstr "An"
521
-
522
-#: searx/templates/legacy/preferences.html:88
523
-#: searx/templates/oscar/preferences.html:117
524
-msgid "Off"
525
-msgstr "Aus"
526
-
527
-#: searx/templates/legacy/result_templates/code.html:3
528
-#: searx/templates/legacy/result_templates/default.html:3
529
-#: searx/templates/legacy/result_templates/map.html:9
530
-#: searx/templates/oscar/macros.html:35 searx/templates/oscar/macros.html:49
531
-msgid "cached"
532
-msgstr "im Cache"
533
-
534
-#: searx/templates/oscar/advanced.html:4
535
-msgid "Advanced settings"
536
-msgstr "Erweiterte Einstellungen"
537
-
538
-#: searx/templates/oscar/base.html:62
539
-#: searx/templates/oscar/messages/first_time.html:4
540
-#: searx/templates/oscar/messages/no_results.html:5
541
-#: searx/templates/oscar/messages/save_settings_successfull.html:5
542
-#: searx/templates/oscar/messages/unknow_error.html:5
543
-msgid "Close"
544
-msgstr "Schließen"
545
-
546
-#: searx/templates/oscar/base.html:64
547
-msgid "Error!"
548
-msgstr "Fehler!"
549
-
550
-#: searx/templates/oscar/base.html:90
551
-msgid "Powered by"
552
-msgstr "Bereitgestellt von"
553
-
554
-#: searx/templates/oscar/base.html:90
555
-msgid "a privacy-respecting, hackable metasearch engine"
556
-msgstr "eine die Privatsphäre respektierende, hackbare Meta-Suchmaschine"
557
-
558
-#: searx/templates/oscar/macros.html:37 searx/templates/oscar/macros.html:51
559
-msgid "proxied"
560
-msgstr "via Proxy-Server"
561
-
562
-#: searx/templates/oscar/preferences.html:12
563
-#: searx/templates/oscar/preferences.html:21
564
-msgid "General"
565
-msgstr "Allgemein"
566
-
567
-#: searx/templates/oscar/preferences.html:13
568
-#: searx/templates/oscar/preferences.html:133
569
-msgid "Engines"
570
-msgstr "Suchmaschinen"
571
-
572
-#: searx/templates/oscar/preferences.html:14
573
-#: searx/templates/oscar/preferences.html:204
574
-msgid "Plugins"
575
-msgstr "Plug-ins"
576
-
577
-#: searx/templates/oscar/preferences.html:15
578
-#: searx/templates/oscar/preferences.html:230
579
-msgid "Answerers"
580
-msgstr "Instant Answers/Sofortantworten"
581
-
582
-#: searx/templates/oscar/preferences.html:16
583
-#: searx/templates/oscar/preferences.html:257
584
-msgid "Cookies"
585
-msgstr "Cookies"
586
-
587
-#: searx/templates/oscar/preferences.html:41
588
-msgid "What language do you prefer for search?"
589
-msgstr "Welche Sprache möchten Sie für die Suche verwenden?"
590
-
591
-#: searx/templates/oscar/preferences.html:47
592
-msgid "Change the language of the layout"
593
-msgstr "Sprache des Layouts ändern"
594
-
595
-#: searx/templates/oscar/preferences.html:57
596
-msgid "Find stuff as you type"
597
-msgstr "Bereits während der Eingabe suchen"
598
-
599
-#: searx/templates/oscar/preferences.html:68
600
-msgid "Proxying image results through searx"
601
-msgstr "Bilder-Suchergebnisse über den searx-Proxy-Server laden"
602
-
603
-#: searx/templates/oscar/preferences.html:77
604
-msgid ""
605
-"Change how forms are submited, <a "
606
-"href=\"http://en.wikipedia.org/wiki/Hypertext_Transfer_Protocol#Request_methods\""
607
-" rel=\"external\">learn more about request methods</a>"
608
-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>"
609
-
610
-#: searx/templates/oscar/preferences.html:86
611
-msgid "Filter content"
612
-msgstr "Inhalte filtern"
613
-
614
-#: searx/templates/oscar/preferences.html:96
615
-msgid "Change searx layout"
616
-msgstr "searx-Layout ändern"
617
-
618
-#: searx/templates/oscar/preferences.html:105
619
-#: searx/templates/oscar/preferences.html:110
620
-msgid "Choose style for this theme"
621
-msgstr "Stilrichtung für diese Benutzeroberfläche auswählen"
622
-
623
-#: searx/templates/oscar/preferences.html:105
624
-#: searx/templates/oscar/preferences.html:110
625
-msgid "Style"
626
-msgstr "Stilrichtung"
627
-
628
-#: searx/templates/oscar/preferences.html:150
629
-#: searx/templates/oscar/preferences.html:161
630
-msgid "Shortcut"
631
-msgstr "Kürzel"
632
-
633
-#: searx/templates/oscar/preferences.html:151
634
-#: searx/templates/oscar/preferences.html:160
635
-msgid "Supports selected language"
636
-msgstr "Unterstützt die ausgewähle Sprache"
637
-
638
-#: searx/templates/oscar/preferences.html:153
639
-msgid "Time range"
640
-msgstr "Zeitraum"
641
-
642
-#: searx/templates/oscar/preferences.html:154
643
-#: searx/templates/oscar/preferences.html:158
644
-msgid "Avg. time"
645
-msgstr "Durchschn. Zeit"
646
-
647
-#: searx/templates/oscar/preferences.html:155
648
-#: searx/templates/oscar/preferences.html:157
649
-msgid "Max time"
650
-msgstr "Maximale Zeit"
651
-
652
-#: searx/templates/oscar/preferences.html:233
653
-msgid "This is the list of searx's instant answering modules."
654
-msgstr "Auflistung der searx-Module für Sofortantworten:"
655
-
656
-#: searx/templates/oscar/preferences.html:237
657
-msgid "Name"
658
-msgstr "Name"
659
-
660
-#: searx/templates/oscar/preferences.html:238
661
-msgid "Keywords"
662
-msgstr "Schlüsselwörter"
663
-
664
-#: searx/templates/oscar/preferences.html:239
665
-msgid "Description"
666
-msgstr "Beschreibung"
667
-
668
-#: searx/templates/oscar/preferences.html:240
669
-msgid "Examples"
670
-msgstr "Beispiele"
671
-
672
-#: searx/templates/oscar/preferences.html:260
673
-msgid ""
674
-"This is the list of cookies and their values searx is storing on your "
675
-"computer."
676
-msgstr "Hier werden die Cookies und die gespeicherten Cookie-Informationen aufgelistet, die searx auf Ihrem Computer speichert."
677
-
678
-#: searx/templates/oscar/preferences.html:261
679
-msgid "With that list, you can assess searx transparency."
680
-msgstr "Mit Hilfe dieser Auflistung können Sie die Transparenz der searx-Suche einschätzen."
681
-
682
-#: searx/templates/oscar/preferences.html:266
683
-msgid "Cookie name"
684
-msgstr "Cookie-Name"
685
-
686
-#: searx/templates/oscar/preferences.html:267
687
-msgid "Value"
688
-msgstr "Wert"
689
-
690
-#: searx/templates/oscar/results.html:7
691
-msgid "Search results"
692
-msgstr "Durchsuche Ergebnisse"
693
-
694
-#: searx/templates/oscar/results.html:119
695
-msgid "Links"
696
-msgstr "Links"
697
-
698
-#: searx/templates/oscar/search.html:6
699
-#: searx/templates/oscar/search_full.html:11
700
-msgid "Start search"
701
-msgstr "Suche starten"
702
-
703
-#: searx/templates/oscar/stats.html:2
704
-msgid "stats"
705
-msgstr "Statistiken"
706
-
707
-#: searx/templates/oscar/time-range.html:3
708
-msgid "Anytime"
709
-msgstr "Beliebiger Zeitunkt"
710
-
711
-#: searx/templates/oscar/time-range.html:6
712
-msgid "Last day"
713
-msgstr "Gestern"
714
-
715
-#: searx/templates/oscar/time-range.html:9
716
-msgid "Last week"
717
-msgstr "Letzte Woche"
718
-
719
-#: searx/templates/oscar/time-range.html:12
720
-msgid "Last month"
721
-msgstr "Letzter Monat"
722
-
723
-#: searx/templates/oscar/time-range.html:15
724
-msgid "Last year"
725
-msgstr "Letztes Jahr"
726
-
727
-#: searx/templates/oscar/messages/first_time.html:6
728
-#: searx/templates/oscar/messages/no_data_available.html:3
729
-msgid "Heads up!"
730
-msgstr "Aufgepasst!"
731
-
732
-#: searx/templates/oscar/messages/first_time.html:7
733
-msgid "It look like you are using searx first time."
734
-msgstr "Anscheinend benutzen Sie searx zum ersten Mal."
735
-
736
-#: searx/templates/oscar/messages/no_cookies.html:3
737
-msgid "Information!"
738
-msgstr "Zur Information!"
739
-
740
-#: searx/templates/oscar/messages/no_cookies.html:4
741
-msgid "currently, there are no cookies defined."
742
-msgstr "Zur Zeit sind keine Cookies definiert."
743
-
744
-#: searx/templates/oscar/messages/no_data_available.html:4
745
-msgid "There is currently no data available. "
746
-msgstr "Zur Zeit sind keine Daten verfügbar."
747
-
748
-#: searx/templates/oscar/messages/no_results.html:7
749
-msgid "Sorry!"
750
-msgstr "Entschuldigung!"
751
-
752
-#: searx/templates/oscar/messages/no_results.html:8
753
-msgid ""
754
-"we didn't find any results. Please use another query or search in more "
755
-"categories."
756
-msgstr "Leider konnten wir keine Suchergebnisse finden. Bitte verwenden Sie eine andere Suchabfrage oder erweitern Sie die Suche auf mehr Kategorien."
757
-
758
-#: searx/templates/oscar/messages/save_settings_successfull.html:7
759
-msgid "Well done!"
760
-msgstr "Gut gemacht!"
761
-
762
-#: searx/templates/oscar/messages/save_settings_successfull.html:8
763
-msgid "Settings saved successfully."
764
-msgstr "Einstellungen erfolgreich gespeichert."
765
-
766
-#: searx/templates/oscar/messages/unknow_error.html:7
767
-msgid "Oh snap!"
768
-msgstr "Hoppla!"
769
-
770
-#: searx/templates/oscar/messages/unknow_error.html:8
771
-msgid "Something went wrong."
772
-msgstr "Ein Fehler ist aufgetreten."
773
-
774
-#: searx/templates/oscar/result_templates/default.html:7
775
-msgid "show media"
776
-msgstr "Medien anzeigen"
777
-
778
-#: searx/templates/oscar/result_templates/default.html:7
779
-msgid "hide media"
780
-msgstr "Medien verbergen"
781
-
782
-#: searx/templates/oscar/result_templates/images.html:30
783
-msgid "Get image"
784
-msgstr "Bild herunterladen"
785
-
786
-#: searx/templates/oscar/result_templates/images.html:33
787
-msgid "View source"
788
-msgstr "Quelle anzeigen"
789
-
790
-#: searx/templates/oscar/result_templates/map.html:7
791
-msgid "show map"
792
-msgstr "Karte anzeigen"
793
-
794
-#: searx/templates/oscar/result_templates/map.html:7
795
-msgid "hide map"
796
-msgstr "Karte verbergen"
797
-
798
-#: searx/templates/oscar/result_templates/map.html:11
799
-msgid "show details"
800
-msgstr "Details anzeigen"
801
-
802
-#: searx/templates/oscar/result_templates/map.html:11
803
-msgid "hide details"
804
-msgstr "Details verbergen"
805
-
806
-#: searx/templates/oscar/result_templates/torrent.html:7
807
-msgid "Filesize"
808
-msgstr "Dateigröße"
809
-
810
-#: searx/templates/oscar/result_templates/torrent.html:9
811
-msgid "Bytes"
812
-msgstr "Bytes"
813
-
814
-#: searx/templates/oscar/result_templates/torrent.html:10
815
-msgid "kiB"
816
-msgstr "kiB"
817
-
818
-#: searx/templates/oscar/result_templates/torrent.html:11
819
-msgid "MiB"
820
-msgstr "MiB"
821
-
822
-#: searx/templates/oscar/result_templates/torrent.html:12
823
-msgid "GiB"
824
-msgstr "GiB"
825
-
826
-#: searx/templates/oscar/result_templates/torrent.html:13
827
-msgid "TiB"
828
-msgstr "TiB"
829
-
830
-#: searx/templates/oscar/result_templates/torrent.html:15
831
-msgid "Number of Files"
832
-msgstr "Anzahl Dateien"
833
-
834
-#: searx/templates/oscar/result_templates/videos.html:7
835
-msgid "show video"
836
-msgstr "Video anzeigen"
837
-
838
-#: searx/templates/oscar/result_templates/videos.html:7
839
-msgid "hide video"
840
-msgstr "Video verbergen"
841
-
842
-#: searx/templates/pix-art/results.html:28
843
-msgid "Load more..."
844
-msgstr "Mehr anzeigen ..."

+ 18
- 0
searx/utils.py View File

1
 import csv
1
 import csv
2
+import hashlib
3
+import hmac
2
 import os
4
 import os
3
 import re
5
 import re
4
 
6
 
290
         return 0
292
         return 0
291
 
293
 
292
 
294
 
295
+# convert a variable to integer or return 0 if it's not a number
296
+def int_or_zero(num):
297
+    if isinstance(num, list):
298
+        if len(num) < 1:
299
+            return 0
300
+        num = num[0]
301
+    return convert_str_to_int(num)
302
+
303
+
293
 def is_valid_lang(lang):
304
 def is_valid_lang(lang):
294
     is_abbr = (len(lang) == 2)
305
     is_abbr = (len(lang) == 2)
295
     if is_abbr:
306
     if is_abbr:
312
     module = load_source(modname, filepath)
323
     module = load_source(modname, filepath)
313
     module.name = modname
324
     module.name = modname
314
     return module
325
     return module
326
+
327
+
328
+def new_hmac(secret_key, url):
329
+    if sys.version_info[0] == 2:
330
+        return hmac.new(bytes(secret_key), url, hashlib.sha256).hexdigest()
331
+    else:
332
+        return hmac.new(bytes(secret_key, 'utf-8'), url, hashlib.sha256).hexdigest()

+ 6
- 4
searx/webapp.py View File

69
 from searx.preferences import Preferences, ValidationException
69
 from searx.preferences import Preferences, ValidationException
70
 from searx.answerers import answerers
70
 from searx.answerers import answerers
71
 from searx.url_utils import urlencode, urlparse, urljoin
71
 from searx.url_utils import urlencode, urlparse, urljoin
72
+from searx.utils import new_hmac
72
 
73
 
73
 # check if the pyopenssl package is installed.
74
 # check if the pyopenssl package is installed.
74
 # It is needed for SSL connection without trouble, see #298
75
 # It is needed for SSL connection without trouble, see #298
290
     if settings.get('result_proxy'):
291
     if settings.get('result_proxy'):
291
         return proxify(url)
292
         return proxify(url)
292
 
293
 
293
-    h = hmac.new(settings['server']['secret_key'], url.encode('utf-8'), hashlib.sha256).hexdigest()
294
+    h = new_hmac(settings['server']['secret_key'], url.encode('utf-8'))
294
 
295
 
295
     return '{0}?{1}'.format(url_for('image_proxy'),
296
     return '{0}?{1}'.format(url_for('image_proxy'),
296
                             urlencode(dict(url=url.encode('utf-8'), h=h)))
297
                             urlencode(dict(url=url.encode('utf-8'), h=h)))
704
     if not url:
705
     if not url:
705
         return '', 400
706
         return '', 400
706
 
707
 
707
-    h = hmac.new(settings['server']['secret_key'], url, hashlib.sha256).hexdigest()
708
+    h = new_hmac(settings['server']['secret_key'], url)
708
 
709
 
709
     if h != request.args.get('h'):
710
     if h != request.args.get('h'):
710
         return '', 400
711
         return '', 400
731
         logger.debug('image-proxy: wrong content-type: {0}'.format(resp.headers.get('content-type')))
732
         logger.debug('image-proxy: wrong content-type: {0}'.format(resp.headers.get('content-type')))
732
         return '', 400
733
         return '', 400
733
 
734
 
734
-    img = ''
735
+    img = b''
735
     chunk_counter = 0
736
     chunk_counter = 0
736
 
737
 
737
     for chunk in resp.iter_content(1024 * 1024):
738
     for chunk in resp.iter_content(1024 * 1024):
792
 @app.route('/favicon.ico')
793
 @app.route('/favicon.ico')
793
 def favicon():
794
 def favicon():
794
     return send_from_directory(os.path.join(app.root_path,
795
     return send_from_directory(os.path.join(app.root_path,
795
-                                            'static/themes',
796
+                                            static_path,
797
+                                            'themes',
796
                                             get_current_theme_name(),
798
                                             get_current_theme_name(),
797
                                             'img'),
799
                                             'img'),
798
                                'favicon.png',
800
                                'favicon.png',

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

1
+# -*- coding: utf-8 -*-
2
+from collections import defaultdict
3
+import mock
4
+from searx.engines import base
5
+from searx.testing import SearxTestCase
6
+
7
+
8
+class TestBaseEngine(SearxTestCase):
9
+
10
+    def test_request(self):
11
+        query = 'test_query'
12
+        dicto = defaultdict(dict)
13
+        dicto['pageno'] = 1
14
+        params = base.request(query, dicto)
15
+        self.assertIn('url', params)
16
+        self.assertIn('base-search.net', params['url'])
17
+
18
+    def test_response(self):
19
+        self.assertRaises(AttributeError, base.response, None)
20
+        self.assertRaises(AttributeError, base.response, [])
21
+        self.assertRaises(AttributeError, base.response, '')
22
+        self.assertRaises(AttributeError, base.response, '[]')
23
+
24
+        response = mock.Mock(text='<response></response>')
25
+        self.assertEqual(base.response(response), [])
26
+
27
+        xml_mock = """<?xml version="1.0"?>
28
+<response>
29
+  <lst name="responseHeader">
30
+    <int name="status">0</int>
31
+    <int name="QTime">1</int>
32
+  </lst>
33
+  <result name="response" numFound="1" start="0">
34
+    <doc>
35
+      <date name="dchdate">2000-01-01T01:01:01Z</date>
36
+      <str name="dcdocid">1</str>
37
+      <str name="dccontinent">cna</str>
38
+      <str name="dccountry">us</str>
39
+      <str name="dccollection">ftciteseerx</str>
40
+      <str name="dcprovider">CiteSeerX</str>
41
+      <str name="dctitle">Science and more</str>
42
+      <arr name="dccreator">
43
+        <str>Someone</str>
44
+      </arr>
45
+      <arr name="dcperson">
46
+        <str>Someone</str>
47
+      </arr>
48
+      <arr name="dcsubject">
49
+        <str>Science and more</str>
50
+      </arr>
51
+      <str name="dcdescription">Science, and even more.</str>
52
+      <arr name="dccontributor">
53
+        <str>The neighbour</str>
54
+      </arr>
55
+      <str name="dcdate">2001</str>
56
+      <int name="dcyear">2001</int>
57
+      <arr name="dctype">
58
+        <str>text</str>
59
+      </arr>
60
+      <arr name="dctypenorm">
61
+        <str>1</str>
62
+      </arr>
63
+      <arr name="dcformat">
64
+        <str>application/pdf</str>
65
+      </arr>
66
+      <arr name="dccontenttype">
67
+        <str>application/pdf</str>
68
+      </arr>
69
+      <arr name="dcidentifier">
70
+        <str>http://example.org/</str>
71
+      </arr>
72
+      <str name="dclink">http://example.org</str>
73
+      <str name="dcsource">http://example.org</str>
74
+      <arr name="dclanguage">
75
+        <str>en</str>
76
+      </arr>
77
+      <str name="dcrights">Under the example.org licence</str>
78
+      <int name="dcoa">1</int>
79
+      <arr name="dclang">
80
+        <str>eng</str>
81
+      </arr>
82
+    </doc>
83
+  </result>
84
+</response>"""
85
+
86
+        response = mock.Mock(text=xml_mock.encode('utf-8'))
87
+        results = base.response(response)
88
+        self.assertEqual(type(results), list)
89
+        self.assertEqual(len(results), 1)
90
+        self.assertEqual(results[0]['title'], 'Science and more')
91
+        self.assertEqual(results[0]['content'], 'Science, and even more.')

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

8
 class TestBingImagesEngine(SearxTestCase):
8
 class TestBingImagesEngine(SearxTestCase):
9
 
9
 
10
     def test_request(self):
10
     def test_request(self):
11
+        bing_images.supported_languages = ['fr-FR', 'en-US']
12
+
11
         query = 'test_query'
13
         query = 'test_query'
12
         dicto = defaultdict(dict)
14
         dicto = defaultdict(dict)
13
         dicto['pageno'] = 1
15
         dicto['pageno'] = 1
14
-        dicto['language'] = 'fr_FR'
16
+        dicto['language'] = 'fr-FR'
15
         dicto['safesearch'] = 1
17
         dicto['safesearch'] = 1
16
         dicto['time_range'] = ''
18
         dicto['time_range'] = ''
17
         params = bing_images.request(query, dicto)
19
         params = bing_images.request(query, dicto)
19
         self.assertTrue(query in params['url'])
21
         self.assertTrue(query in params['url'])
20
         self.assertTrue('bing.com' in params['url'])
22
         self.assertTrue('bing.com' in params['url'])
21
         self.assertTrue('SRCHHPGUSR' in params['cookies'])
23
         self.assertTrue('SRCHHPGUSR' in params['cookies'])
22
-        self.assertTrue('fr' in params['cookies']['SRCHHPGUSR'])
24
+        self.assertTrue('DEMOTE' in params['cookies']['SRCHHPGUSR'])
25
+        self.assertTrue('_EDGE_S' in params['cookies'])
26
+        self.assertTrue('fr-fr' in params['cookies']['_EDGE_S'])
27
+
28
+        dicto['language'] = 'fr'
29
+        params = bing_images.request(query, dicto)
30
+        self.assertTrue('_EDGE_S' in params['cookies'])
31
+        self.assertTrue('fr-fr' in params['cookies']['_EDGE_S'])
23
 
32
 
24
         dicto['language'] = 'all'
33
         dicto['language'] = 'all'
25
         params = bing_images.request(query, dicto)
34
         params = bing_images.request(query, dicto)
26
-        self.assertIn('SRCHHPGUSR', params['cookies'])
27
-        self.assertIn('en', params['cookies']['SRCHHPGUSR'])
35
+        self.assertTrue('_EDGE_S' in params['cookies'])
36
+        self.assertTrue('en-us' in params['cookies']['_EDGE_S'])
28
 
37
 
29
     def test_response(self):
38
     def test_response(self):
30
         self.assertRaises(AttributeError, bing_images.response, None)
39
         self.assertRaises(AttributeError, bing_images.response, None)
82
         self.assertEqual(results[0]['content'], '')
91
         self.assertEqual(results[0]['content'], '')
83
         self.assertEqual(results[0]['thumbnail_src'], 'thumb_url')
92
         self.assertEqual(results[0]['thumbnail_src'], 'thumb_url')
84
         self.assertEqual(results[0]['img_src'], 'img_url')
93
         self.assertEqual(results[0]['img_src'], 'img_url')
94
+
95
+    def test_fetch_supported_languages(self):
96
+        html = """
97
+        <div>
98
+            <div id="region-section-content">
99
+                <ul class="b_vList">
100
+                    <li>
101
+                        <a href="https://bing...&setmkt=de-DE&s...">Germany</a>
102
+                        <a href="https://bing...&setmkt=nb-NO&s...">Norway</a>
103
+                    </li>
104
+                </ul>
105
+                <ul class="b_vList">
106
+                    <li>
107
+                        <a href="https://bing...&setmkt=es-AR&s...">Argentina</a>
108
+                    </li>
109
+                </ul>
110
+            </div>
111
+        </div>
112
+        """
113
+        response = mock.Mock(text=html)
114
+        languages = list(bing_images._fetch_supported_languages(response))
115
+        self.assertEqual(len(languages), 3)
116
+        self.assertIn('de-DE', languages)
117
+        self.assertIn('no-NO', languages)
118
+        self.assertIn('es-AR', languages)

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

8
 class TestBingVideosEngine(SearxTestCase):
8
 class TestBingVideosEngine(SearxTestCase):
9
 
9
 
10
     def test_request(self):
10
     def test_request(self):
11
+        bing_videos.supported_languages = ['fr-FR', 'en-US']
12
+
11
         query = 'test_query'
13
         query = 'test_query'
12
         dicto = defaultdict(dict)
14
         dicto = defaultdict(dict)
13
         dicto['pageno'] = 1
15
         dicto['pageno'] = 1

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

1
-from collections import defaultdict
2
-import mock
3
-from searx.engines import blekko_images
4
-from searx.testing import SearxTestCase
5
-
6
-
7
-class TestBlekkoImagesEngine(SearxTestCase):
8
-
9
-    def test_request(self):
10
-        query = 'test_query'
11
-        dicto = defaultdict(dict)
12
-        dicto['pageno'] = 0
13
-        dicto['safesearch'] = 1
14
-        params = blekko_images.request(query, dicto)
15
-        self.assertIn('url', params)
16
-        self.assertIn(query, params['url'])
17
-        self.assertIn('blekko.com', params['url'])
18
-        self.assertIn('page', params['url'])
19
-
20
-        dicto['pageno'] = 1
21
-        params = blekko_images.request(query, dicto)
22
-        self.assertNotIn('page', params['url'])
23
-
24
-    def test_response(self):
25
-        self.assertRaises(AttributeError, blekko_images.response, None)
26
-        self.assertRaises(AttributeError, blekko_images.response, [])
27
-        self.assertRaises(AttributeError, blekko_images.response, '')
28
-        self.assertRaises(AttributeError, blekko_images.response, '[]')
29
-
30
-        response = mock.Mock(text='[]')
31
-        self.assertEqual(blekko_images.response(response), [])
32
-
33
-        json = """
34
-        [
35
-            {
36
-                "c": 1,
37
-                "page_url": "http://result_url.html",
38
-                "title": "Photo title",
39
-                "tn_url": "http://ts1.mm.bing.net/th?id=HN.608050619474382748&pid=15.1",
40
-                "url": "http://result_image.jpg"
41
-            },
42
-            {
43
-                "c": 2,
44
-                "page_url": "http://companyorange.simpsite.nl/OSM",
45
-                "title": "OSM",
46
-                "tn_url": "http://ts2.mm.bing.net/th?id=HN.608048068264919461&pid=15.1",
47
-                "url": "http://simpsite.nl/userdata2/58985/Home/OSM.bmp"
48
-            },
49
-            {
50
-                "c": 3,
51
-                "page_url": "http://invincible.webklik.nl/page/osm",
52
-                "title": "OSM",
53
-                "tn_url": "http://ts1.mm.bing.net/th?id=HN.608024514657649476&pid=15.1",
54
-                "url": "http://www.webklik.nl/user_files/2009_09/65324/osm.gif"
55
-            },
56
-            {
57
-                "c": 4,
58
-                "page_url": "http://www.offshorenorway.no/event/companyDetail/id/12492",
59
-                "title": "Go to OSM Offshore AS homepage",
60
-                "tn_url": "http://ts2.mm.bing.net/th?id=HN.608054265899847285&pid=15.1",
61
-                "url": "http://www.offshorenorway.no/firmalogo/OSM-logo.png"
62
-            }
63
-        ]
64
-        """
65
-        response = mock.Mock(text=json)
66
-        results = blekko_images.response(response)
67
-        self.assertEqual(type(results), list)
68
-        self.assertEqual(len(results), 4)
69
-        self.assertEqual(results[0]['title'], 'Photo title')
70
-        self.assertEqual(results[0]['url'], 'http://result_url.html')
71
-        self.assertEqual(results[0]['img_src'], 'http://result_image.jpg')

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

40
         response = mock.Mock(text='{"data": []}')
40
         response = mock.Mock(text='{"data": []}')
41
         self.assertEqual(faroo.response(response), [])
41
         self.assertEqual(faroo.response(response), [])
42
 
42
 
43
-        response = mock.Mock(text='{"data": []}', status_code=401)
44
-        self.assertRaises(Exception, faroo.response, response)
45
-
46
         response = mock.Mock(text='{"data": []}', status_code=429)
43
         response = mock.Mock(text='{"data": []}', status_code=429)
47
         self.assertRaises(Exception, faroo.response, response)
44
         self.assertRaises(Exception, faroo.response, response)
48
 
45
 
98
         response = mock.Mock(text=json)
95
         response = mock.Mock(text=json)
99
         results = faroo.response(response)
96
         results = faroo.response(response)
100
         self.assertEqual(type(results), list)
97
         self.assertEqual(type(results), list)
101
-        self.assertEqual(len(results), 4)
98
+        self.assertEqual(len(results), 3)
102
         self.assertEqual(results[0]['title'], 'This is the title')
99
         self.assertEqual(results[0]['title'], 'This is the title')
103
         self.assertEqual(results[0]['url'], 'http://this.is.the.url/')
100
         self.assertEqual(results[0]['url'], 'http://this.is.the.url/')
104
         self.assertEqual(results[0]['content'], 'This is the content')
101
         self.assertEqual(results[0]['content'], 'This is the content')
105
         self.assertEqual(results[1]['title'], 'This is the title2')
102
         self.assertEqual(results[1]['title'], 'This is the title2')
106
         self.assertEqual(results[1]['url'], 'http://this.is.the.url2/')
103
         self.assertEqual(results[1]['url'], 'http://this.is.the.url2/')
107
         self.assertEqual(results[1]['content'], 'This is the content2')
104
         self.assertEqual(results[1]['content'], 'This is the content2')
108
-        self.assertEqual(results[3]['img_src'], 'http://upload.wikimedia.org/optimized.jpg')
105
+        self.assertEqual(results[2]['thumbnail'], 'http://upload.wikimedia.org/optimized.jpg')
109
 
106
 
110
         json = """
107
         json = """
111
         {}
108
         {}

+ 56
- 6
tests/unit/engines/test_google_news.py
File diff suppressed because it is too large
View File


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

13
         params = nyaa.request(query, dic)
13
         params = nyaa.request(query, dic)
14
         self.assertTrue('url' in params)
14
         self.assertTrue('url' in params)
15
         self.assertTrue(query in params['url'])
15
         self.assertTrue(query in params['url'])
16
-        self.assertTrue('nyaa.se' in params['url'])
16
+        self.assertTrue('nyaa.si' in params['url'])
17
 
17
 
18
     def test_response(self):
18
     def test_response(self):
19
         resp = mock.Mock(text='<html></html>')
19
         resp = mock.Mock(text='<html></html>')
20
         self.assertEqual(nyaa.response(resp), [])
20
         self.assertEqual(nyaa.response(resp), [])
21
 
21
 
22
         html = """
22
         html = """
23
-        <table class="tlist">
24
-          <tbody>
25
-            <tr class="trusted tlistrow">
26
-              <td class="tlisticon">
27
-                <a href="//www.nyaa.se" title="English-translated Anime">
28
-                   <img src="//files.nyaa.se" alt="English-translated Anime">
29
-                </a>
30
-              </td>
31
-              <td class="tlistname">
32
-                <a href="//www.nyaa.se/?page3">
33
-                  Sample torrent title
34
-                </a>
35
-              </td>
36
-              <td class="tlistdownload">
37
-                <a href="//www.nyaa.se/?page_dl" title="Download">
38
-                  <img src="//files.nyaa.se/www-dl.png" alt="DL">
39
-                </a>
40
-              </td>
41
-              <td class="tlistsize">10 MiB</td>
42
-              <td class="tlistsn">1</td>
43
-              <td class="tlistln">3</td>
44
-              <td class="tlistdn">666</td>
45
-              <td class="tlistmn">0</td>
46
-            </tr>
47
-          </tbody>
23
+        <table class="table table-bordered table-hover table-striped torrent-list">
24
+        <thead>
25
+        <tr>
26
+        <th class="hdr-category text-center" style="width:80px;">
27
+        <div>Category</div>
28
+        </th>
29
+        <th class="hdr-name" style="width:auto;">
30
+        <div>Name</div>
31
+        </th>
32
+        <th class="hdr-comments sorting text-center" title="Comments" style="width:50px;">
33
+        <a href="/?f=0&amp;c=0_0&amp;q=Death+Parade&amp;s=comments&amp;o=desc"></a>
34
+        <i class="fa fa-comments-o"></i>
35
+        </th>
36
+        <th class="hdr-link text-center" style="width:70px;">
37
+        <div>Link</div>
38
+        </th>
39
+        <th class="hdr-size sorting text-center" style="width:100px;">
40
+        <a href="/?f=0&amp;c=0_0&amp;q=Death+Parade&amp;s=size&amp;o=desc"></a>
41
+        <div>Size</div>
42
+        </th>
43
+        <th class="hdr-date sorting_desc text-center" title="In local time" style="width:140px;">
44
+        <a href="/?f=0&amp;c=0_0&amp;q=Death+Parade&amp;s=id&amp;o=asc"></a>
45
+        <div>Date</div>
46
+        </th>
47
+        <th class="hdr-seeders sorting text-center" title="Seeders" style="width:50px;">
48
+        <a href="/?f=0&amp;c=0_0&amp;q=Death+Parade&amp;s=seeders&amp;o=desc"></a>
49
+        <i class="fa fa-arrow-up" aria-hidden="true"></i>
50
+        </th>
51
+        <th class="hdr-leechers sorting text-center" title="Leechers" style="width:50px;">
52
+        <a href="/?f=0&amp;c=0_0&amp;q=Death+Parade&amp;s=leechers&amp;o=desc"></a>
53
+        <i class="fa fa-arrow-down" aria-hidden="true"></i>
54
+        </th>
55
+        <th class="hdr-downloads sorting text-center" title="Completed downloads" style="width:50px;">
56
+        <a href="/?f=0&amp;c=0_0&amp;q=Death+Parade&amp;s=downloads&amp;o=desc"></a>
57
+        <i class="fa fa-check" aria-hidden="true"></i>
58
+        </th>
59
+        </tr>
60
+        </thead>
61
+        <tbody>
62
+        <tr class="default">
63
+        <td style="padding:0 4px;">
64
+        <a href="/?c=1_2" title="Anime - English-translated">
65
+        <img src="/static/img/icons/nyaa/1_2.png" alt="Anime - English-translated">
66
+        </a>
67
+        </td>
68
+        <td colspan="2">
69
+        <a href="/view/1" title="Sample title 1">Sample title 1</a>
70
+        </td>
71
+        <td class="text-center" style="white-space: nowrap;">
72
+        <a href="/download/1.torrent"><i class="fa fa-fw fa-download"></i></a>
73
+        <a href="magnet:?xt=urn:btih:2"><i class="fa fa-fw fa-magnet"></i></a>
74
+        </td>
75
+        <td class="text-center">723.7 MiB</td>
76
+        <td class="text-center" data-timestamp="1503307456" title="1 week 3
77
+        days 9 hours 44 minutes 39 seconds ago">2017-08-21 11:24</td>
78
+        <td class="text-center" style="color: green;">1</td>
79
+        <td class="text-center" style="color: red;">3</td>
80
+        <td class="text-center">12</td>
81
+        </tr>
82
+        <tr class="default">
83
+        <td style="padding:0 4px;">
84
+        <a href="/?c=1_2" title="Anime - English-translated">
85
+        <img src="/static/img/icons/nyaa/1_2.png" alt="Anime - English-translated">
86
+        </a>
87
+        </td>
88
+        <td colspan="2">
89
+        <a href="/view/2" title="Sample title 2">Sample title 2</a>
90
+        </td>
91
+        <td class="text-center" style="white-space: nowrap;">
92
+        <a href="magnet:?xt=urn:btih:2"><i class="fa fa-fw fa-magnet"></i></a>
93
+        </td>
94
+        <td class="text-center">8.2 GiB</td>
95
+        <td class="text-center" data-timestamp="1491608400" title="4 months 3
96
+        weeks 4 days 19 hours 28 minutes 55 seconds ago">2017-04-08 01:40</td>
97
+        <td class="text-center" style="color: green;">10</td>
98
+        <td class="text-center" style="color: red;">1</td>
99
+        <td class="text-center">206</td>
100
+        </tr>
101
+        </tbody>
48
         </table>
102
         </table>
49
         """
103
         """
50
 
104
 
52
         results = nyaa.response(resp)
106
         results = nyaa.response(resp)
53
 
107
 
54
         self.assertEqual(type(results), list)
108
         self.assertEqual(type(results), list)
55
-        self.assertEqual(len(results), 1)
109
+        self.assertEqual(len(results), 2)
56
 
110
 
57
         r = results[0]
111
         r = results[0]
58
-        self.assertTrue(r['url'].find('www.nyaa.se/?page3') >= 0)
59
-        self.assertTrue(r['torrentfile'].find('www.nyaa.se/?page_dl') >= 0)
60
-        self.assertTrue(r['content'].find('English-translated Anime') >= 0)
61
-        self.assertTrue(r['content'].find('Downloaded 666 times.') >= 0)
112
+        self.assertTrue(r['url'].find('1') >= 0)
113
+        self.assertTrue(r['torrentfile'].find('1.torrent') >= 0)
114
+        self.assertTrue(r['content'].find('Anime - English-translated') >= 0)
115
+        self.assertTrue(r['content'].find('Downloaded 12 times.') >= 0)
62
 
116
 
63
-        self.assertEqual(r['title'], 'Sample torrent title')
117
+        self.assertEqual(r['title'], 'Sample title 1')
64
         self.assertEqual(r['seed'], 1)
118
         self.assertEqual(r['seed'], 1)
65
         self.assertEqual(r['leech'], 3)
119
         self.assertEqual(r['leech'], 3)
66
-        self.assertEqual(r['filesize'], 10 * 1024 * 1024)
120
+        self.assertEqual(r['filesize'], 723700000)
121
+
122
+        r = results[1]
123
+        self.assertTrue(r['url'].find('2') >= 0)
124
+        self.assertTrue(r['magnetlink'].find('magnet:') >= 0)

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

139
             <div id="regions-popup">
139
             <div id="regions-popup">
140
                 <div>
140
                 <div>
141
                     <ul>
141
                     <ul>
142
-                        <li><a data-val="browser"></a></li>
143
-                        <li><a data-val="de-CH"></a></li>
144
-                        <li><a data-val="fr-CH"></a></li>
142
+                        <li><a data-search-language="browser"></a></li>
143
+                        <li><a data-search-language="de-CH"></a></li>
144
+                        <li><a data-search-language="fr-CH"></a></li>
145
                     </ul>
145
                     </ul>
146
                 </div>
146
                 </div>
147
             </div>
147
             </div>

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

14
         params = torrentz.request(query, dic)
14
         params = torrentz.request(query, dic)
15
         self.assertTrue('url' in params)
15
         self.assertTrue('url' in params)
16
         self.assertTrue(query in params['url'])
16
         self.assertTrue(query in params['url'])
17
-        self.assertTrue('torrentz.eu' in params['url'])
17
+        self.assertTrue('torrentz2.eu' in params['url'])
18
 
18
 
19
     def test_response(self):
19
     def test_response(self):
20
         resp = mock.Mock(text='<html></html>')
20
         resp = mock.Mock(text='<html></html>')
30
               books ebooks
30
               books ebooks
31
             </dt>
31
             </dt>
32
             <dd>
32
             <dd>
33
-              <span class="v">1</span>
34
-              <span class="a">
35
-                <span title="Sun, 22 Nov 2015 03:01:42">4 months</span>
36
-              </span>
37
-              <span class="s">30 MB</span>
38
-              <span class="u">14</span>
39
-              <span class="d">1</span>
33
+              <span>1</span>
34
+              <span title="1503595924">5 hours</span>
35
+              <span>30 MB</span>
36
+              <span>14</span>
37
+              <span>1</span>
40
             </dd>
38
             </dd>
41
           </dl>
39
           </dl>
42
 
40
 
48
               books ebooks
46
               books ebooks
49
             </dt>
47
             </dt>
50
             <dd>
48
             <dd>
51
-              <span class="v">1</span>
52
-              <span class="a">
53
-                <span title="Sun, 2124091j0j190gm42">4 months</span>
54
-              </span>
55
-              <span class="s">30MB</span>
56
-              <span class="u">5,555</span>
57
-              <span class="d">1,234,567</span>
49
+              <span>1</span>
50
+              <span title="1503595924 aaa">5 hours</span>
51
+              <span>30MB</span>
52
+              <span>5,555</span>
53
+              <span>1,234,567</span>
58
             </dd>
54
             </dd>
59
           </dl>
55
           </dl>
60
         </div>
56
         </div>
68
 
64
 
69
         # testing against the first result
65
         # testing against the first result
70
         r = results[0]
66
         r = results[0]
71
-        self.assertEqual(r['url'], 'https://torrentz.eu/4362e08b1d80e1820fb2550b752f9f3126fe76d6')
67
+        self.assertEqual(r['url'], 'https://torrentz2.eu/4362e08b1d80e1820fb2550b752f9f3126fe76d6')
72
         self.assertEqual(r['title'], 'Completely valid info books ebooks')
68
         self.assertEqual(r['title'], 'Completely valid info books ebooks')
73
         # 22 Nov 2015 03:01:42
69
         # 22 Nov 2015 03:01:42
74
-        self.assertEqual(r['publishedDate'], datetime(2015, 11, 22, 3, 1, 42))
70
+        self.assertEqual(r['publishedDate'], datetime.fromtimestamp(1503595924))
75
         self.assertEqual(r['seed'], 14)
71
         self.assertEqual(r['seed'], 14)
76
         self.assertEqual(r['leech'], 1)
72
         self.assertEqual(r['leech'], 1)
77
         self.assertEqual(r['filesize'], 30 * 1024 * 1024)
73
         self.assertEqual(r['filesize'], 30 * 1024 * 1024)
79
 
75
 
80
         # testing against the second result
76
         # testing against the second result
81
         r = results[1]
77
         r = results[1]
82
-        self.assertEqual(r['url'], 'https://torrentz.eu/poaskdpokaspod')
78
+        self.assertEqual(r['url'], 'https://torrentz2.eu/poaskdpokaspod')
83
         self.assertEqual(r['title'], 'Invalid hash and date and filesize books ebooks')
79
         self.assertEqual(r['title'], 'Invalid hash and date and filesize books ebooks')
84
         self.assertEqual(r['seed'], 5555)
80
         self.assertEqual(r['seed'], 5555)
85
         self.assertEqual(r['leech'], 1234567)
81
         self.assertEqual(r['leech'], 1234567)

+ 8
- 8
utils/fetch_languages.py View File

8
 # are written in current directory to avoid overwriting in case something goes wrong.
8
 # are written in current directory to avoid overwriting in case something goes wrong.
9
 
9
 
10
 from requests import get
10
 from requests import get
11
-from urllib import urlencode
12
 from lxml.html import fromstring
11
 from lxml.html import fromstring
13
-from json import loads, dumps
12
+from json import loads, dump
14
 import io
13
 import io
15
 from sys import path
14
 from sys import path
16
 path.append('../searx')  # noqa
15
 path.append('../searx')  # noqa
17
 from searx import settings
16
 from searx import settings
17
+from searx.url_utils import urlencode
18
 from searx.engines import initialize_engines, engines
18
 from searx.engines import initialize_engines, engines
19
 
19
 
20
 # Geonames API for country names.
20
 # Geonames API for country names.
70
     json = loads(response.text)
70
     json = loads(response.text)
71
     content = json.get('geonames', None)
71
     content = json.get('geonames', None)
72
     if content is None or len(content) != 1:
72
     if content is None or len(content) != 1:
73
-        print "No country name found for " + locale[0] + "-" + locale[1]
73
+        print("No country name found for " + locale[0] + "-" + locale[1])
74
         return ''
74
         return ''
75
 
75
 
76
     return content[0].get('countryName', '')
76
     return content[0].get('countryName', '')
84
             try:
84
             try:
85
                 engines_languages[engine_name] = engines[engine_name].fetch_supported_languages()
85
                 engines_languages[engine_name] = engines[engine_name].fetch_supported_languages()
86
             except Exception as e:
86
             except Exception as e:
87
-                print e
87
+                print(e)
88
 
88
 
89
     # write json file
89
     # write json file
90
     with io.open(engines_languages_file, "w", encoding="utf-8") as f:
90
     with io.open(engines_languages_file, "w", encoding="utf-8") as f:
91
-        f.write(unicode(dumps(engines_languages, ensure_ascii=False, encoding="utf-8")))
91
+        dump(engines_languages, f, ensure_ascii=False)
92
 
92
 
93
 
93
 
94
 # Join all language lists.
94
 # Join all language lists.
97
     global languages
97
     global languages
98
     # include wikipedia first for more accurate language names
98
     # include wikipedia first for more accurate language names
99
     languages = {code: lang for code, lang
99
     languages = {code: lang for code, lang
100
-                 in engines_languages['wikipedia'].iteritems()
100
+                 in engines_languages['wikipedia'].items()
101
                  if valid_code(code)}
101
                  if valid_code(code)}
102
 
102
 
103
     for engine_name in engines_languages:
103
     for engine_name in engines_languages:
121
     # filter list to include only languages supported by most engines
121
     # filter list to include only languages supported by most engines
122
     min_supported_engines = int(0.70 * len(engines_languages))
122
     min_supported_engines = int(0.70 * len(engines_languages))
123
     languages = {code: lang for code, lang
123
     languages = {code: lang for code, lang
124
-                 in languages.iteritems()
124
+                 in languages.items()
125
                  if len(lang.get('counter', [])) >= min_supported_engines or
125
                  if len(lang.get('counter', [])) >= min_supported_engines or
126
                  len(languages.get(code.split('-')[0], {}).get('counter', [])) >= min_supported_engines}
126
                  len(languages.get(code.split('-')[0], {}).get('counter', [])) >= min_supported_engines}
127
 
127
 
165
 
165
 
166
 # Write languages.py.
166
 # Write languages.py.
167
 def write_languages_file():
167
 def write_languages_file():
168
-    new_file = open(languages_file, 'w')
168
+    new_file = open(languages_file, 'wb')
169
     file_content = '# -*- coding: utf-8 -*-\n'\
169
     file_content = '# -*- coding: utf-8 -*-\n'\
170
                    + '# list of language codes\n'\
170
                    + '# list of language codes\n'\
171
                    + '# this file is generated automatically by utils/update_search_languages.py\n'\
171
                    + '# this file is generated automatically by utils/update_search_languages.py\n'\

+ 3
- 3
utils/update-translations.sh View File

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