commit: a88768efd8ee6b832febda8508cb1ba3c8778b94
parent f90eb428c679d3852d9738f6279d045283340562
Author: Alexandre Flament <alex@al-f.net>
Date: Sat, 22 Oct 2016 14:25:50 +0200
Merge branch 'master' into http1.1
Diffstat:
95 files changed, 999 insertions(+), 491 deletions(-)
diff --git a/.travis.yml b/.travis.yml
@@ -4,6 +4,8 @@ cache:
- npm
- directories:
- $HOME/.cache/pip
+addons:
+ firefox: "latest"
language: python
python:
- "2.7"
@@ -12,6 +14,9 @@ before_install:
- "sh -e /etc/init.d/xvfb start"
- npm install less grunt-cli
- ( cd searx/static/themes/oscar;npm install; cd - )
+ - mkdir -p ~/drivers; export PATH=~/drivers:$PATH;
+ - GECKODRIVER_URL="https://github.com/mozilla/geckodriver/releases/download/v0.11.1/geckodriver-v0.11.1-linux64.tar.gz";
+ - FILE=`mktemp`; wget "$GECKODRIVER_URL" -qO $FILE && tar xz -C ~/drivers -f $FILE geckodriver; rm $FILE; chmod 777 ~/drivers/geckodriver;
install:
- ./manage.sh update_dev_packages
- pip install coveralls
diff --git a/AUTHORS.rst b/AUTHORS.rst
@@ -58,3 +58,5 @@ generally made searx better:
- marc @a01200356
- Harry Wood @harry-wood
- Thomas Renard @threnard
+- Pydo `<https://github.com/pydo>`_
+- Athemis `<https://github.com/Athemis>`_
diff --git a/manage.sh b/manage.sh
@@ -53,8 +53,8 @@ build_style() {
styles() {
echo '[!] Building styles'
- build_style themes/default/less/style.less themes/default/css/style.css
- build_style themes/default/less/style-rtl.less themes/default/css/style-rtl.css
+ build_style themes/legacy/less/style.less themes/legacy/css/style.css
+ build_style themes/legacy/less/style-rtl.less themes/legacy/css/style-rtl.css
build_style themes/courgette/less/style.less themes/courgette/css/style.css
build_style themes/courgette/less/style-rtl.less themes/courgette/css/style-rtl.css
build_style less/bootstrap/bootstrap.less css/bootstrap.min.css
diff --git a/requirements-dev.txt b/requirements-dev.txt
@@ -3,8 +3,8 @@ mock==2.0.0
nose2[coverage-plugin]
pep8==1.7.0
plone.testing==5.0.0
-robotframework-selenium2library==1.7.4
+robotframework-selenium2library==1.8.0
robotsuite==1.7.0
-transifex-client==0.11
+transifex-client==0.12.2
unittest2==1.1.0
-zope.testrunner==4.4.10
+zope.testrunner==4.5.1
diff --git a/requirements.txt b/requirements.txt
@@ -1,4 +1,4 @@
-certifi==2016.2.28
+certifi==2016.9.26
flask==0.11.1
flask-babel==0.11.1
lxml==3.6.0
diff --git a/searx/engines/__init__.py b/searx/engines/__init__.py
@@ -57,11 +57,17 @@ def load_module(filename):
def load_engine(engine_data):
- engine_name = engine_data['engine']
+
+ if '_' in engine_data['name']:
+ logger.error('Engine name conains underscore: "{}"'.format(engine_data['name']))
+ sys.exit(1)
+
+ engine_module = engine_data['engine']
+
try:
- engine = load_module(engine_name + '.py')
+ engine = load_module(engine_module + '.py')
except:
- logger.exception('Cannot load engine "{}"'.format(engine_name))
+ logger.exception('Cannot load engine "{}"'.format(engine_module))
return None
for param_name in engine_data:
diff --git a/searx/engines/digbt.py b/searx/engines/digbt.py
@@ -40,7 +40,7 @@ def response(resp):
results = list()
for result in search_res:
url = urljoin(URL, result.xpath('.//a[@title]/@href')[0])
- title = result.xpath('.//a[@title]/text()')[0]
+ title = extract_text(result.xpath('.//a[@title]'))
content = extract_text(result.xpath('.//div[@class="files"]'))
files_data = extract_text(result.xpath('.//div[@class="tail"]')).split()
filesize = get_torrent_size(files_data[FILESIZE], files_data[FILESIZE_MULTIPLIER])
diff --git a/searx/engines/kickass.py b/searx/engines/kickass.py
@@ -16,13 +16,14 @@ from urllib import quote
from lxml import html
from operator import itemgetter
from searx.engines.xpath import extract_text
+from searx.utils import get_torrent_size, convert_str_to_int
# engine dependent config
categories = ['videos', 'music', 'files']
paging = True
# search-url
-url = 'https://kickass.to/'
+url = 'https://kickass.cd/'
search_url = url + 'search/{search_term}/{pageno}/'
# specific xpath variables
@@ -57,41 +58,16 @@ def response(resp):
href = urljoin(url, link.attrib['href'])
title = extract_text(link)
content = escape(extract_text(result.xpath(content_xpath)))
- seed = result.xpath('.//td[contains(@class, "green")]/text()')[0]
- leech = result.xpath('.//td[contains(@class, "red")]/text()')[0]
- filesize = result.xpath('.//td[contains(@class, "nobr")]/text()')[0]
- filesize_multiplier = result.xpath('.//td[contains(@class, "nobr")]//span/text()')[0]
- files = result.xpath('.//td[contains(@class, "center")][2]/text()')[0]
-
- # convert seed to int if possible
- if seed.isdigit():
- seed = int(seed)
- else:
- seed = 0
+ seed = extract_text(result.xpath('.//td[contains(@class, "green")]'))
+ leech = extract_text(result.xpath('.//td[contains(@class, "red")]'))
+ filesize_info = extract_text(result.xpath('.//td[contains(@class, "nobr")]'))
+ files = extract_text(result.xpath('.//td[contains(@class, "center")][2]'))
- # convert leech to int if possible
- if leech.isdigit():
- leech = int(leech)
- else:
- leech = 0
-
- # convert filesize to byte if possible
- try:
- filesize = float(filesize)
-
- # convert filesize to byte
- if filesize_multiplier == 'TB':
- filesize = int(filesize * 1024 * 1024 * 1024 * 1024)
- elif filesize_multiplier == 'GB':
- filesize = int(filesize * 1024 * 1024 * 1024)
- elif filesize_multiplier == 'MB':
- filesize = int(filesize * 1024 * 1024)
- elif filesize_multiplier == 'KB':
- filesize = int(filesize * 1024)
- except:
- filesize = None
-
- # convert files to int if possible
+ seed = convert_str_to_int(seed)
+ leech = convert_str_to_int(leech)
+
+ filesize, filesize_multiplier = filesize_info.split()
+ filesize = get_torrent_size(filesize, filesize_multiplier)
if files.isdigit():
files = int(files)
else:
diff --git a/searx/engines/pdbe.py b/searx/engines/pdbe.py
@@ -0,0 +1,109 @@
+"""
+ PDBe (Protein Data Bank in Europe)
+
+ @website https://www.ebi.ac.uk/pdbe
+ @provide-api yes (https://www.ebi.ac.uk/pdbe/api/doc/search.html),
+ unlimited
+ @using-api yes
+ @results python dictionary (from json)
+ @stable yes
+ @parse url, title, content, img_src
+"""
+
+from json import loads
+from flask_babel import gettext
+
+categories = ['science']
+
+hide_obsolete = False
+
+# status codes of unpublished entries
+pdb_unpublished_codes = ['HPUB', 'HOLD', 'PROC', 'WAIT', 'AUTH', 'AUCO', 'REPL', 'POLC', 'REFI', 'TRSF', 'WDRN']
+# url for api query
+pdbe_solr_url = 'https://www.ebi.ac.uk/pdbe/search/pdb/select?'
+# base url for results
+pdbe_entry_url = 'https://www.ebi.ac.uk/pdbe/entry/pdb/{pdb_id}'
+# link to preview image of structure
+pdbe_preview_url = 'https://www.ebi.ac.uk/pdbe/static/entry/{pdb_id}_deposited_chain_front_image-200x200.png'
+
+
+def request(query, params):
+
+ params['url'] = pdbe_solr_url
+ params['method'] = 'POST'
+ params['data'] = {
+ 'q': query,
+ 'wt': "json" # request response in parsable format
+ }
+ return params
+
+
+def construct_body(result):
+ # set title
+ title = result['title']
+
+ # construct content body
+ content = """{title}<br />{authors} {journal} <strong>{volume}</strong> {page} ({year})"""
+
+ # replace placeholders with actual content
+ try:
+ if result['journal']:
+ content = content.format(
+ title=result['citation_title'],
+ authors=result['entry_author_list'][0], journal=result['journal'], volume=result['journal_volume'],
+ page=result['journal_page'], year=result['citation_year'])
+ else:
+ content = content.format(
+ title=result['citation_title'],
+ authors=result['entry_author_list'][0], journal='', volume='', page='', year=result['release_year'])
+ img_src = pdbe_preview_url.format(pdb_id=result['pdb_id'])
+ except (KeyError):
+ content = None
+ img_src = None
+
+ # construct url for preview image
+ try:
+ img_src = pdbe_preview_url.format(pdb_id=result['pdb_id'])
+ except (KeyError):
+ img_src = None
+
+ return [title, content, img_src]
+
+
+def response(resp):
+
+ results = []
+ json = loads(resp.text)['response']['docs']
+
+ # parse results
+ for result in json:
+ # catch obsolete entries and mark them accordingly
+ if result['status'] in pdb_unpublished_codes:
+ continue
+ if hide_obsolete:
+ continue
+ if result['status'] == 'OBS':
+ # expand title to add some sort of warning message
+ title = gettext('{title} (OBSOLETE)').format(title=result['title'])
+ superseded_url = pdbe_entry_url.format(pdb_id=result['superseded_by'])
+
+ # since we can't construct a proper body from the response, we'll make up our own
+ msg_superseded = gettext("This entry has been superseded by")
+ content = '<em>{msg_superseded} \<a href="{url}">{pdb_id}</a></em>'.format(
+ msg_superseded=msg_superseded,
+ url=superseded_url,
+ pdb_id=result['superseded_by'], )
+
+ # obsoleted entries don't have preview images
+ img_src = None
+ else:
+ title, content, img_src = construct_body(result)
+
+ results.append({
+ 'url': pdbe_entry_url.format(pdb_id=result['pdb_id']),
+ 'title': title,
+ 'content': content,
+ 'img_src': img_src
+ })
+
+ return results
diff --git a/searx/engines/seedpeer.py b/searx/engines/seedpeer.py
@@ -0,0 +1,78 @@
+# Seedpeer (Videos, Music, Files)
+#
+# @website http://seedpeer.eu
+# @provide-api no (nothing found)
+#
+# @using-api no
+# @results HTML (using search portal)
+# @stable yes (HTML can change)
+# @parse url, title, content, seed, leech, magnetlink
+
+from urlparse import urljoin
+from cgi import escape
+from urllib import quote
+from lxml import html
+from operator import itemgetter
+from searx.engines.xpath import extract_text
+
+
+url = 'http://www.seedpeer.eu/'
+search_url = url + 'search/{search_term}/7/{page_no}.html'
+# specific xpath variables
+torrent_xpath = '//*[@id="body"]/center/center/table[2]/tr/td/a'
+alternative_torrent_xpath = '//*[@id="body"]/center/center/table[1]/tr/td/a'
+title_xpath = '//*[@id="body"]/center/center/table[2]/tr/td/a/text()'
+alternative_title_xpath = '//*[@id="body"]/center/center/table/tr/td/a'
+seeds_xpath = '//*[@id="body"]/center/center/table[2]/tr/td[4]/font/text()'
+alternative_seeds_xpath = '//*[@id="body"]/center/center/table/tr/td[4]/font/text()'
+peers_xpath = '//*[@id="body"]/center/center/table[2]/tr/td[5]/font/text()'
+alternative_peers_xpath = '//*[@id="body"]/center/center/table/tr/td[5]/font/text()'
+age_xpath = '//*[@id="body"]/center/center/table[2]/tr/td[2]/text()'
+alternative_age_xpath = '//*[@id="body"]/center/center/table/tr/td[2]/text()'
+size_xpath = '//*[@id="body"]/center/center/table[2]/tr/td[3]/text()'
+alternative_size_xpath = '//*[@id="body"]/center/center/table/tr/td[3]/text()'
+
+
+# do search-request
+def request(query, params):
+ params['url'] = search_url.format(search_term=quote(query),
+ page_no=params['pageno'] - 1)
+ return params
+
+
+# get response from search-request
+def response(resp):
+ results = []
+ dom = html.fromstring(resp.text)
+ torrent_links = dom.xpath(torrent_xpath)
+ if len(torrent_links) > 0:
+ seeds = dom.xpath(seeds_xpath)
+ peers = dom.xpath(peers_xpath)
+ titles = dom.xpath(title_xpath)
+ sizes = dom.xpath(size_xpath)
+ ages = dom.xpath(age_xpath)
+ else: # under ~5 results uses a different xpath
+ torrent_links = dom.xpath(alternative_torrent_xpath)
+ seeds = dom.xpath(alternative_seeds_xpath)
+ peers = dom.xpath(alternative_peers_xpath)
+ titles = dom.xpath(alternative_title_xpath)
+ sizes = dom.xpath(alternative_size_xpath)
+ ages = dom.xpath(alternative_age_xpath)
+ # return empty array if nothing is found
+ if not torrent_links:
+ return []
+
+ # parse results
+ for index, result in enumerate(torrent_links):
+ link = result.attrib.get('href')
+ href = urljoin(url, link)
+ results.append({'url': href,
+ 'title': titles[index].text_content(),
+ 'content': '{}, {}'.format(sizes[index], ages[index]),
+ 'seed': seeds[index],
+ 'leech': peers[index],
+
+ 'template': 'torrent.html'})
+
+ # return results sorted by seeder
+ return sorted(results, key=itemgetter('seed'), reverse=True)
diff --git a/searx/settings.yml b/searx/settings.yml
@@ -18,6 +18,12 @@ ui:
default_theme : oscar # ui theme
default_locale : "" # Default interface locale - leave blank to detect from browser information or use codes from the 'locales' config section
+# searx supports result proxification using an external service: https://github.com/asciimoo/morty
+# uncomment below section if you have running morty proxy
+#result_proxy:
+# url : http://127.0.0.1:3000/
+# key : your_morty_proxy_key
+
outgoing: # communication with search engines
request_timeout : 2.0 # seconds
useragent_suffix : "" # suffix of searx_useragent, could contain informations like an email address to the administrator
@@ -301,6 +307,12 @@ engines:
timeout : 6.0
disabled : True
+ - name: kickass
+ engine : kickass
+ shortcut : kc
+ timeout : 4.0
+ disabled : True
+
- name : microsoft academic
engine : json_engine
paging : True
@@ -339,6 +351,13 @@ engines:
disabled : True
shortcut : or
+ - name : pdbe
+ engine : pdbe
+ shortcut : pdb
+# Hide obsolete PDB entries.
+# Default is not to hide obsolete structures
+# hide_obsolete : False
+
- name : photon
engine : photon
shortcut : ph
@@ -377,7 +396,7 @@ engines:
timeout : 10.0
disabled : True
- - name : scanr_structures
+ - name : scanr structures
shortcut: scs
engine : scanr_structures
disabled : True
@@ -495,6 +514,12 @@ engines:
timeout: 6.0
categories : science
+ - name : seedpeer
+ engine : seedpeer
+ shortcut: speu
+ categories: files, music, videos
+ disabled: True
+
- name : dictzone
engine : dictzone
shortcut : dc
diff --git a/searx/settings_robot.yml b/searx/settings_robot.yml
@@ -15,7 +15,7 @@ server:
ui:
themes_path : ""
- default_theme : default
+ default_theme : legacy
default_locale : ""
outgoing:
@@ -23,12 +23,12 @@ outgoing:
useragent_suffix : ""
engines:
- - name : general_dummy
+ - name : general dummy
engine : dummy
categories : general
shortcut : gd
- - name : dummy_dummy
+ - name : dummy dummy
engine : dummy
categories : dummy
shortcut : dd
diff --git a/searx/static/themes/default/css/style-rtl.css b/searx/static/themes/legacy/css/style-rtl.css
diff --git a/searx/static/themes/default/css/style.css b/searx/static/themes/legacy/css/style.css
diff --git a/searx/static/themes/default/img/favicon.png b/searx/static/themes/legacy/img/favicon.png
Binary files differ.
diff --git a/searx/static/themes/default/img/github_ribbon.png b/searx/static/themes/legacy/img/github_ribbon.png
Binary files differ.
diff --git a/searx/static/themes/default/img/icons/icon_500px.ico b/searx/static/themes/legacy/img/icons/icon_500px.ico
Binary files differ.
diff --git a/searx/static/themes/default/img/icons/icon_bing.ico b/searx/static/themes/legacy/img/icons/icon_bing.ico
Binary files differ.
diff --git a/searx/static/themes/default/img/icons/icon_dailymotion.ico b/searx/static/themes/legacy/img/icons/icon_dailymotion.ico
Binary files differ.
diff --git a/searx/static/themes/default/img/icons/icon_deezer.ico b/searx/static/themes/legacy/img/icons/icon_deezer.ico
Binary files differ.
diff --git a/searx/static/themes/default/img/icons/icon_deviantart.ico b/searx/static/themes/legacy/img/icons/icon_deviantart.ico
Binary files differ.
diff --git a/searx/static/themes/default/img/icons/icon_digg.ico b/searx/static/themes/legacy/img/icons/icon_digg.ico
Binary files differ.
diff --git a/searx/static/themes/default/img/icons/icon_duckduckgo.ico b/searx/static/themes/legacy/img/icons/icon_duckduckgo.ico
Binary files differ.
diff --git a/searx/static/themes/default/img/icons/icon_flickr.ico b/searx/static/themes/legacy/img/icons/icon_flickr.ico
Binary files differ.
diff --git a/searx/static/themes/default/img/icons/icon_github.ico b/searx/static/themes/legacy/img/icons/icon_github.ico
Binary files differ.
diff --git a/searx/static/themes/default/img/icons/icon_google play apps.ico b/searx/static/themes/legacy/img/icons/icon_google play apps.ico
Binary files differ.
diff --git a/searx/static/themes/default/img/icons/icon_google play movies.ico b/searx/static/themes/legacy/img/icons/icon_google play movies.ico
Binary files differ.
diff --git a/searx/static/themes/default/img/icons/icon_google play music.ico b/searx/static/themes/legacy/img/icons/icon_google play music.ico
Binary files differ.
diff --git a/searx/static/themes/default/img/icons/icon_google.ico b/searx/static/themes/legacy/img/icons/icon_google.ico
Binary files differ.
diff --git a/searx/static/themes/default/img/icons/icon_kickass.ico b/searx/static/themes/legacy/img/icons/icon_kickass.ico
Binary files differ.
diff --git a/searx/static/themes/default/img/icons/icon_openstreetmap.ico b/searx/static/themes/legacy/img/icons/icon_openstreetmap.ico
Binary files differ.
diff --git a/searx/static/themes/default/img/icons/icon_searchcode code.ico b/searx/static/themes/legacy/img/icons/icon_searchcode code.ico
Binary files differ.
diff --git a/searx/static/themes/default/img/icons/icon_searchcode doc.ico b/searx/static/themes/legacy/img/icons/icon_searchcode doc.ico
Binary files differ.
diff --git a/searx/static/themes/default/img/icons/icon_searchcode.ico b/searx/static/themes/legacy/img/icons/icon_searchcode.ico
Binary files differ.
diff --git a/searx/static/themes/default/img/icons/icon_soundcloud.ico b/searx/static/themes/legacy/img/icons/icon_soundcloud.ico
Binary files differ.
diff --git a/searx/static/themes/default/img/icons/icon_stackoverflow.ico b/searx/static/themes/legacy/img/icons/icon_stackoverflow.ico
Binary files differ.
diff --git a/searx/static/themes/default/img/icons/icon_startpage.ico b/searx/static/themes/legacy/img/icons/icon_startpage.ico
Binary files differ.
diff --git a/searx/static/themes/default/img/icons/icon_subtitleseeker.ico b/searx/static/themes/legacy/img/icons/icon_subtitleseeker.ico
Binary files differ.
diff --git a/searx/static/themes/default/img/icons/icon_twitter.ico b/searx/static/themes/legacy/img/icons/icon_twitter.ico
Binary files differ.
diff --git a/searx/static/themes/default/img/icons/icon_vimeo.ico b/searx/static/themes/legacy/img/icons/icon_vimeo.ico
Binary files differ.
diff --git a/searx/static/themes/default/img/icons/icon_wikipedia.ico b/searx/static/themes/legacy/img/icons/icon_wikipedia.ico
Binary files differ.
diff --git a/searx/static/themes/default/img/icons/icon_yahoo.ico b/searx/static/themes/legacy/img/icons/icon_yahoo.ico
Binary files differ.
diff --git a/searx/static/themes/default/img/icons/icon_youtube.ico b/searx/static/themes/legacy/img/icons/icon_youtube.ico
Binary files differ.
diff --git a/searx/static/themes/default/img/preference-icon.png b/searx/static/themes/legacy/img/preference-icon.png
Binary files differ.
diff --git a/searx/static/themes/default/img/search-icon.png b/searx/static/themes/legacy/img/search-icon.png
Binary files differ.
diff --git a/searx/static/themes/default/img/searx.png b/searx/static/themes/legacy/img/searx.png
Binary files differ.
diff --git a/searx/static/themes/default/img/searx_logo.svg b/searx/static/themes/legacy/img/searx_logo.svg
diff --git a/searx/static/themes/default/js/searx.js b/searx/static/themes/legacy/js/searx.js
diff --git a/searx/static/themes/default/less/autocompleter.less b/searx/static/themes/legacy/less/autocompleter.less
diff --git a/searx/static/themes/default/less/code.less b/searx/static/themes/legacy/less/code.less
diff --git a/searx/static/themes/default/less/definitions.less b/searx/static/themes/legacy/less/definitions.less
diff --git a/searx/static/themes/default/less/mixins.less b/searx/static/themes/legacy/less/mixins.less
diff --git a/searx/static/themes/default/less/search.less b/searx/static/themes/legacy/less/search.less
diff --git a/searx/static/themes/default/less/style-rtl.less b/searx/static/themes/legacy/less/style-rtl.less
diff --git a/searx/static/themes/default/less/style.less b/searx/static/themes/legacy/less/style.less
diff --git a/searx/templates/courgette/base.html b/searx/templates/courgette/base.html
@@ -22,7 +22,7 @@
{% endblock %}
{% block meta %}{% endblock %}
{% block head %}
- <link title="searx" type="application/opensearchdescription+xml" rel="search" href="{{ url_for('opensearch') }}"/>
+ <link title="{{ instance_name }}" type="application/opensearchdescription+xml" rel="search" href="{{ url_for('opensearch') }}"/>
{% endblock %}
<script type="text/javascript">
searx = {};
diff --git a/searx/templates/default/404.html b/searx/templates/default/404.html
@@ -1,9 +0,0 @@
-{% extends "default/base.html" %}
-{% block content %}
-<div class="center">
- <h1>{{ _('Page not found') }}</h1>
- {% autoescape false %}
- <p>{{ _('Go to %(search_page)s.', search_page='<a href="{}">{}</a>'.decode('utf-8').format(url_for('index'), _('search page'))) }}</p>
- {% endautoescape %}
-</div>
-{% endblock %}
diff --git a/searx/templates/default/about.html b/searx/templates/default/about.html
@@ -1,66 +0,0 @@
-{% extends 'default/base.html' %}
-{% block content %}
-{% include 'default/github_ribbon.html' %}
-<div class="row"{% if rtl %} dir="ltr"{% endif %}>
- <h1>About <a href="{{ url_for('index') }}">searx</a></h1>
-
- <p>Searx is a <a href="https://en.wikipedia.org/wiki/Metasearch_engine">metasearch engine</a>, aggregating the results of other <a href="{{ url_for('preferences') }}">search engines</a> while not storing information about its users.
- </p>
- <h2>Why use searx?</h2>
- <ul>
- <li>searx may not offer you as personalised results as Google, but it doesn't generate a profile about you</li>
- <li>searx doesn't care about what you search for, never shares anything with a third party, and it can't be used to compromise you</li>
- <li>searx is free software, the code is 100% open and you can help to make it better. See more on <a href="https://github.com/asciimoo/searx">github</a></li>
- </ul>
- <p>If you do care about privacy, want to be a conscious user, or otherwise believe
- in digital freedom, make searx your default search engine or run it on your own server</p>
-
-<h2>Technical details - How does it work?</h2>
-
-<p>Searx is a <a href="https://en.wikipedia.org/wiki/Metasearch_engine">metasearch engine</a>,
-inspired by the <a href="http://seeks-project.info/">seeks project</a>.<br />
-It provides basic privacy by mixing your queries with searches on other platforms without storing search data. Queries are made using a POST request on every browser (except chrome*). Therefore they show up in neither our logs, nor your url history. In case of Chrome* users there is an exception, if searx used from the search bar it performs GET requests.<br />
-Searx can be added to your browser's search bar; moreover, it can be set as the default search engine.
-</p>
-
-<h2>How can I make it my own?</h2>
-
-<p>Searx appreciates your concern regarding logs, so take the <a href="https://github.com/asciimoo/searx">code</a> and run it yourself! <br />Add your Searx to this <a href="https://github.com/asciimoo/searx/wiki/Searx-instances">list</a> to help other people reclaim their privacy and make the Internet freer!
-<br />The more decentralized Internet is the more freedom we have!</p>
-
-
-<h2>More about searx</h2>
-
-<ul>
- <li><a href="https://github.com/asciimoo/searx">github</a></li>
- <li><a href="https://www.ohloh.net/p/searx/">ohloh</a></li>
- <li><a href="https://twitter.com/Searx_engine">twitter</a></li>
- <li>IRC: #searx @ freenode (<a href="https://kiwiirc.com/client/irc.freenode.com/searx">webclient</a>)</li>
- <li><a href="https://www.transifex.com/projects/p/searx/">transifex</a></li>
-</ul>
-
-
-<hr />
-
-<h2 id="faq">FAQ</h2>
-
-<h3>How to add to firefox?</h3>
-<p><a href="#" onclick="window.external.AddSearchProvider(window.location.protocol + '//' + window.location.host + '{{ url_for('opensearch') }}');">Install</a> searx as a search engine on any version of Firefox! (javascript required)</p>
-
-<h2 id="dev_faq">Developer FAQ</h2>
-
-<h3>New engines?</h3>
-<ul>
- <li>Edit your <a href="https://raw.github.com/asciimoo/searx/master/searx/settings.yml">settings.yml</a></li>
- <li>Create your custom engine module, check the <a href="https://github.com/asciimoo/searx/blob/master/examples/basic_engine.py">example engine</a></li>
-</ul>
-<p>Don't forget to restart searx after config edit!</p>
-
-<h3>Installation/WSGI support?</h3>
-<p>See the <a href="https://github.com/asciimoo/searx/wiki/Installation">installation and setup</a> wiki page</p>
-
-<h3>How to debug engines?</h3>
-<p><a href="{{ url_for('stats') }}">Stats page</a> contains some useful data about the engines used.</p>
-
-</div>
-{% endblock %}
diff --git a/searx/templates/default/base.html b/searx/templates/default/base.html
@@ -1,38 +0,0 @@
-<!DOCTYPE html>
-<html xmlns="http://www.w3.org/1999/xhtml" lang="en" xml:lang="en"{% if rtl %} dir="rtl"{% endif %}>
- <head>
- <meta charset="UTF-8" />
- <meta name="description" content="searx - a privacy-respecting, hackable metasearch engine" />
- <meta name="keywords" content="searx, search, search engine, metasearch, meta search" />
- <meta name="generator" content="searx/{{ searx_version }}">
- <meta name="referrer" content="no-referrer">
- <meta name="viewport" content="width=device-width, maximum-scale=1.0, user-scalable=1" />
- <title>{% block title %}{% endblock %}searx</title>
- <link rel="stylesheet" href="{{ url_for('static', filename='css/style.css') }}" type="text/css" media="screen" />
- {% if rtl %}
- <link rel="stylesheet" href="{{ url_for('static', filename='css/style-rtl.css') }}" type="text/css" media="screen" />
- {% endif %}
- <link rel="shortcut icon" href="{{ url_for('static', filename='img/favicon.png') }}?v=2" />
- {% block styles %}
- {% endblock %}
- {% block meta %}{% endblock %}
- {% block head %}
- <link title="searx" type="application/opensearchdescription+xml" rel="search" href="{{ url_for('opensearch') }}"/>
- {% endblock %}
- </head>
- <body>
- <div id="container">
- {% block content %}
- {% endblock %}
- {% if autocomplete %}
- <script src="{{ url_for('static', filename='js/mootools-core-1.4.5-min.js') }}" ></script>
- <script src="{{ url_for('static', filename='js/mootools-autocompleter-1.1.2-min.js') }}" ></script>
- {% endif %}
- <script type="text/javascript">
- searx = {};
- searx.autocompleter = {% if autocomplete %}true{% else %}false{% endif %};
- </script>
- <script src="{{ url_for('static', filename='js/searx.js') }}" ></script>
- </div>
- </body>
-</html>
diff --git a/searx/templates/default/index.html b/searx/templates/default/index.html
@@ -1,18 +0,0 @@
-{% extends "default/base.html" %}
-{% block content %}
-<div class="center">
- <div class="title"><h1>searx</h1></div>
- {% include 'default/search.html' %}
- <p class="top_margin">
- {% if rtl %}
- <a href="{{ url_for('preferences') }}" class="hmarg">{{ _('preferences') }}</a>
- {% endif %}
- <a href="{{ url_for('about') }}" class="hmarg">{{ _('about') }}</a>
- {% if not rtl %}
- <a href="{{ url_for('preferences') }}" class="hmarg">{{ _('preferences') }}</a>
- {% endif %}
- </p>
-</div>
-{% include 'default/github_ribbon.html' %}
-{% endblock %}
-
diff --git a/searx/templates/default/preferences.html b/searx/templates/default/preferences.html
@@ -1,129 +0,0 @@
-{% extends "default/base.html" %}
-{% block head %} {% endblock %}
-{% block content %}
-<div class="row">
- <h2>{{ _('Preferences') }}</h2>
-
- <form method="post" action="{{ url_for('preferences') }}" id="search_form">
- <fieldset>
- <legend>{{ _('Default categories') }}</legend>
- {% set display_tooltip = false %}
- {% include 'default/categories.html' %}
- </fieldset>
- <fieldset>
- <legend>{{ _('Search language') }}</legend>
- <p>
- <select name='language'>
- <option value="all" {% if current_language == 'all' %}selected="selected"{% endif %}>{{ _('Automatic') }}</option>
- {% for lang_id,lang_name,country_name in language_codes | sort(attribute=1) %}
- <option value="{{ lang_id }}" {% if lang_id == current_language %}selected="selected"{% endif %}>{{ lang_name }} ({{ country_name }}) - {{ lang_id }}</option>
- {% endfor %}
- </select>
- </p>
- </fieldset>
- <fieldset>
- <legend>{{ _('Interface language') }}</legend>
- <p>
- <select name='locale'>
- {% for locale_id,locale_name in locales.items() | sort %}
- <option value="{{ locale_id }}" {% if locale_id == current_locale %}selected="selected"{% endif %}>{{ locale_name }}</option>
- {% endfor %}
- </select>
- </p>
- </fieldset>
- <fieldset>
- <legend>{{ _('Autocomplete') }}</legend>
- <p>
- <select name="autocomplete">
- <option value=""> - </option>
- {% for backend in autocomplete_backends %}
- <option value="{{ backend }}" {% if backend == autocomplete %}selected="selected"{% endif %}>{{ backend }}</option>
- {% endfor %}
- </select>
- </p>
- </fieldset>
- <fieldset>
- <legend>{{ _('Image proxy') }}</legend>
- <p>
- <select name='image_proxy'>
- <option value="1" {% if image_proxy %}selected="selected"{% endif %}>{{ _('Enabled') }}</option>
- <option value="" {% if not image_proxy %}selected="selected"{% endif %}>{{ _('Disabled') }}</option>
- </select>
- </p>
- </fieldset>
- <fieldset>
- <legend>{{ _('Method') }}</legend>
- <p>
- <select name='method'>
- <option value="POST" {% if method == 'POST' %}selected="selected"{% endif %}>POST</option>
- <option value="GET" {% if method == 'GET' %}selected="selected"{% endif %}>GET</option>
- </select>
- </p>
- </fieldset>
- <fieldset>
- <legend>{{ _('SafeSearch') }}</legend>
- <p>
- <select name='safesearch'>
- <option value="2" {% if safesearch == '2' %}selected="selected"{% endif %}>{{ _('Strict') }}</option>
- <option value="1" {% if safesearch == '1' %}selected="selected"{% endif %}>{{ _('Moderate') }}</option>
- <option value="0" {% if safesearch == '0' %}selected="selected"{% endif %}>{{ _('None') }}</option>
- </select>
- </p>
- </fieldset>
- <fieldset>
- <legend>{{ _('Themes') }}</legend>
- <p>
- <select name="theme">
- {% for name in themes %}
- <option value="{{ name }}" {% if name == theme %}selected="selected"{% endif %}>{{ name }}</option>
- {% endfor %}
- </select>
- </p>
- </fieldset>
- <fieldset>
- <legend>{{ _('Results on new tabs') }}</legend>
- <p>
- <select name='results_on_new_tab'>
- <option value="1" {% if results_on_new_tab %}selected="selected"{% endif %}>{{ _('On') }}</option>
- <option value="0" {% if not results_on_new_tab %}selected="selected"{% endif %}>{{ _('Off')}}</option>
- </select>
- </p>
- </fieldset>
- <fieldset>
- <legend>{{ _('Currently used search engines') }}</legend>
-
- <table>
- <tr>
- <th>{{ _('Engine name') }}</th>
- <th>{{ _('Category') }}</th>
- <th>{{ _('Allow') }} / {{ _('Block') }}</th>
- </tr>
- {% for categ in all_categories %}
- {% for search_engine in engines_by_category[categ] %}
-
- {% if not search_engine.private %}
- <tr>
- <td>{{ search_engine.name }} ({{ shortcuts[search_engine.name] }})‎</td>
- <td>{{ _(categ) }}</td>
- <td class="engine_checkbox">
- <input type="checkbox" id="engine_{{ categ|replace(' ', '_') }}_{{ search_engine.name|replace(' ', '_') }}" name="engine_{{ search_engine.name }}__{{ categ }}"{% if (search_engine.name, categ) in disabled_engines %} checked="checked"{% endif %} />
- <label class="allow" for="engine_{{ categ|replace(' ', '_') }}_{{ search_engine.name|replace(' ', '_') }}">{{ _('Allow') }}</label>
- <label class="deny" for="engine_{{ categ|replace(' ', '_') }}_{{ search_engine.name|replace(' ', '_') }}">{{ _('Block') }}</label>
- </td>
- </tr>
- {% endif %}
- {% endfor %}
- {% endfor %}
- </table>
- </fieldset>
- <p class="small_font">{{ _('These settings are stored in your cookies, this allows us not to store this data about you.') }}
- <br />
- {{ _("These cookies serve your sole convenience, we don't use these cookies to track you.") }}
- </p>
-
- <input type="submit" value="{{ _('save') }}" />
- <div class="{% if rtl %}left{% else %}right{% endif %} preferences_back"><a href="{{ url_for('clear_cookies') }}">{{ _('Reset defaults') }}</a></div>
- <div class="{% if rtl %}left{% else %}right{% endif %} preferences_back"><a href="{{ url_for('index') }}">{{ _('back') }}</a></div>
- </form>
-</div>
-{% endblock %}
diff --git a/searx/templates/default/results.html b/searx/templates/default/results.html
@@ -1,100 +0,0 @@
-{% extends "default/base.html" %}
-{% block title %}{{ q }} - {% endblock %}
-{% block meta %}<link rel="alternate" type="application/rss+xml" title="Searx search: {{ q }}" href="{{ url_for('index') }}?q={{ q|urlencode }}&format=rss&{% for category in selected_categories %}category_{{ category }}=1&{% endfor %}pageno={{ pageno }}">{% endblock %}
-{% block content %}
-<div class="preferences_container right"><a href="{{ url_for('preferences') }}" id="preferences"><span>preferences</span></a></div>
-<div class="small search center">
- {% include 'default/search.html' %}
-</div>
-<div id="results">
- <div id="sidebar">
-
- <div id="search_url">
- {{ _('Search URL') }}:
- <input type="text" value="{{ base_url }}?q={{ q|urlencode }}{% if selected_categories %}&categories={{ selected_categories|join(",") | replace(' ','+') }}{% endif %}{% if pageno > 1 %}&pageno={{ pageno }}{% endif %}" readonly />
- </div>
- <div id="apis">
- {{ _('Download results') }}
- {% for output_type in ('csv', 'json', 'rss') %}
- <form method="{{ method or 'POST' }}" action="{{ url_for('index') }}">
- <div class="left">
- <input type="hidden" name="q" value="{{ q }}" />
- <input type="hidden" name="format" value="{{ output_type }}" />
- {% for category in selected_categories %}
- <input type="hidden" name="category_{{ category }}" value="1"/>
- {% endfor %}
- <input type="hidden" name="pageno" value="{{ pageno }}" />
- <input type="submit" value="{{ output_type }}" />
- </div>
- </form>
- {% endfor %}
- </div>
- </div>
-
- {% if answers %}
- <div id="answers"><span>{{ _('Answers') }}</span>
- {% for answer in answers %}
- <span>{{ answer }}</span>
- {% endfor %}
- </div>
- {% endif %}
-
- {% if suggestions %}
- <div id="suggestions"><span id="suggestions-title">{{ _('Suggestions') }} : </span>
- {% set first = true %}
- {% for suggestion in suggestions %}
- {% if not first %} • {% endif %}<form method="{{ method or 'POST' }}" action="{{ url_for('index') }}">
- <input type="hidden" name="q" value="{{ suggestion }}">
- <input type="submit" class="suggestion" value="{{ suggestion }}" />
- </form>
- {% set first = false %}
- {% endfor %}
- </div>
- {% endif %}
-
- {% if infoboxes %}
- <div id="infoboxes">
- {% for infobox in infoboxes %}
- {% include 'default/infobox.html' %}
- {% endfor %}
- </div>
- {% endif %}
-
- {% for result in results %}
- {% if result['template'] %}
- {% include get_result_template('default', result['template']) %}
- {% else %}
- {% include 'default/result_templates/default.html' %}
- {% endif %}
- {% endfor %}
-
- {% if paging %}
- <div id="pagination">
- {% if pageno > 1 %}
- <form method="{{ method or 'POST' }}" action="{{ url_for('index') }}">
- <div class="{% if rtl %}right{% else %}left{% endif %}">
- <input type="hidden" name="q" value="{{ q }}" />
- {% for category in selected_categories %}
- <input type="hidden" name="category_{{ category }}" value="1"/>
- {% endfor %}
- <input type="hidden" name="pageno" value="{{ pageno-1 }}" />
- <input type="submit" value="<< {{ _('previous page') }}" />
- </div>
- </form>
- {% endif %}
- <form method="{{ method or 'POST' }}" action="{{ url_for('index') }}">
- <div class="{% if rtl %}left{% else %}right{% endif %}">
- {% for category in selected_categories %}
- <input type="hidden" name="category_{{ category }}" value="1"/>
- {% endfor %}
- <input type="hidden" name="q" value="{{ q }}" />
- <input type="hidden" name="pageno" value="{{ pageno+1 }}" />
- <input type="submit" value="{{ _('next page') }} >>" />
- </div>
- </form>
-
- <br />
- </div>
- {% endif %}
-</div>
-{% endblock %}
diff --git a/searx/templates/default/search.html b/searx/templates/default/search.html
@@ -1,8 +0,0 @@
-<form method="{{ method or 'POST' }}" action="{{ url_for('index') }}" id="search_form">
- <div id="search_wrapper">
- <input type="text" placeholder="{{ _('Search for...') }}" id="q" class="q" name="q" tabindex="1" autocomplete="off" size="100" {% if q %}value="{{ q }}"{% endif %}/>
- <input type="submit" value="search" id="search_submit" />
- </div>
- {% set display_tooltip = true %}
- {% include 'default/categories.html' %}
-</form>
diff --git a/searx/templates/default/stats.html b/searx/templates/default/stats.html
@@ -1,22 +0,0 @@
-{% extends "default/base.html" %}
-{% block head %} {% endblock %}
-{% block content %}
-<h2>{{ _('Engine stats') }}</h2>
-
-{% for stat_name,stat_category in stats %}
-<div class="left">
- <table>
- <tr colspan="3">
- <th>{{ stat_name }}</th>
- </tr>
- {% for engine in stat_category %}
- <tr>
- <td>{{ engine.name }}</td>
- <td>{{ '%.02f'|format(engine.avg) }}</td>
- <td class="percentage"><div style="width: {{ engine.percentage }}%"> </div></td>
- </tr>
- {% endfor %}
- </table>
-</div>
-{% endfor %}
-{% endblock %}
diff --git a/searx/templates/legacy/404.html b/searx/templates/legacy/404.html
@@ -0,0 +1,9 @@
+{% extends "legacy/base.html" %}
+{% block content %}
+<div class="center">
+ <h1>{{ _('Page not found') }}</h1>
+ {% autoescape false %}
+ <p>{{ _('Go to %(search_page)s.', search_page='<a href="{}">{}</a>'.decode('utf-8').format(url_for('index'), _('search page'))) }}</p>
+ {% endautoescape %}
+</div>
+{% endblock %}
diff --git a/searx/templates/legacy/about.html b/searx/templates/legacy/about.html
@@ -0,0 +1,66 @@
+{% extends 'legacy/base.html' %}
+{% block content %}
+{% include 'legacy/github_ribbon.html' %}
+<div class="row"{% if rtl %} dir="ltr"{% endif %}>
+ <h1>About <a href="{{ url_for('index') }}">searx</a></h1>
+
+ <p>Searx is a <a href="https://en.wikipedia.org/wiki/Metasearch_engine">metasearch engine</a>, aggregating the results of other <a href="{{ url_for('preferences') }}">search engines</a> while not storing information about its users.
+ </p>
+ <h2>Why use searx?</h2>
+ <ul>
+ <li>searx may not offer you as personalised results as Google, but it doesn't generate a profile about you</li>
+ <li>searx doesn't care about what you search for, never shares anything with a third party, and it can't be used to compromise you</li>
+ <li>searx is free software, the code is 100% open and you can help to make it better. See more on <a href="https://github.com/asciimoo/searx">github</a></li>
+ </ul>
+ <p>If you do care about privacy, want to be a conscious user, or otherwise believe
+ in digital freedom, make searx your default search engine or run it on your own server</p>
+
+<h2>Technical details - How does it work?</h2>
+
+<p>Searx is a <a href="https://en.wikipedia.org/wiki/Metasearch_engine">metasearch engine</a>,
+inspired by the <a href="http://seeks-project.info/">seeks project</a>.<br />
+It provides basic privacy by mixing your queries with searches on other platforms without storing search data. Queries are made using a POST request on every browser (except chrome*). Therefore they show up in neither our logs, nor your url history. In case of Chrome* users there is an exception, if searx used from the search bar it performs GET requests.<br />
+Searx can be added to your browser's search bar; moreover, it can be set as the default search engine.
+</p>
+
+<h2>How can I make it my own?</h2>
+
+<p>Searx appreciates your concern regarding logs, so take the <a href="https://github.com/asciimoo/searx">code</a> and run it yourself! <br />Add your Searx to this <a href="https://github.com/asciimoo/searx/wiki/Searx-instances">list</a> to help other people reclaim their privacy and make the Internet freer!
+<br />The more decentralized Internet is the more freedom we have!</p>
+
+
+<h2>More about searx</h2>
+
+<ul>
+ <li><a href="https://github.com/asciimoo/searx">github</a></li>
+ <li><a href="https://www.ohloh.net/p/searx/">ohloh</a></li>
+ <li><a href="https://twitter.com/Searx_engine">twitter</a></li>
+ <li>IRC: #searx @ freenode (<a href="https://kiwiirc.com/client/irc.freenode.com/searx">webclient</a>)</li>
+ <li><a href="https://www.transifex.com/projects/p/searx/">transifex</a></li>
+</ul>
+
+
+<hr />
+
+<h2 id="faq">FAQ</h2>
+
+<h3>How to add to firefox?</h3>
+<p><a href="#" onclick="window.external.AddSearchProvider(window.location.protocol + '//' + window.location.host + '{{ url_for('opensearch') }}');">Install</a> searx as a search engine on any version of Firefox! (javascript required)</p>
+
+<h2 id="dev_faq">Developer FAQ</h2>
+
+<h3>New engines?</h3>
+<ul>
+ <li>Edit your <a href="https://raw.github.com/asciimoo/searx/master/searx/settings.yml">settings.yml</a></li>
+ <li>Create your custom engine module, check the <a href="https://github.com/asciimoo/searx/blob/master/examples/basic_engine.py">example engine</a></li>
+</ul>
+<p>Don't forget to restart searx after config edit!</p>
+
+<h3>Installation/WSGI support?</h3>
+<p>See the <a href="https://github.com/asciimoo/searx/wiki/Installation">installation and setup</a> wiki page</p>
+
+<h3>How to debug engines?</h3>
+<p><a href="{{ url_for('stats') }}">Stats page</a> contains some useful data about the engines used.</p>
+
+</div>
+{% endblock %}
diff --git a/searx/templates/legacy/base.html b/searx/templates/legacy/base.html
@@ -0,0 +1,38 @@
+<!DOCTYPE html>
+<html xmlns="http://www.w3.org/1999/xhtml" lang="en" xml:lang="en"{% if rtl %} dir="rtl"{% endif %}>
+ <head>
+ <meta charset="UTF-8" />
+ <meta name="description" content="searx - a privacy-respecting, hackable metasearch engine" />
+ <meta name="keywords" content="searx, search, search engine, metasearch, meta search" />
+ <meta name="generator" content="searx/{{ searx_version }}">
+ <meta name="referrer" content="no-referrer">
+ <meta name="viewport" content="width=device-width, maximum-scale=1.0, user-scalable=1" />
+ <title>{% block title %}{% endblock %}searx</title>
+ <link rel="stylesheet" href="{{ url_for('static', filename='css/style.css') }}" type="text/css" media="screen" />
+ {% if rtl %}
+ <link rel="stylesheet" href="{{ url_for('static', filename='css/style-rtl.css') }}" type="text/css" media="screen" />
+ {% endif %}
+ <link rel="shortcut icon" href="{{ url_for('static', filename='img/favicon.png') }}?v=2" />
+ {% block styles %}
+ {% endblock %}
+ {% block meta %}{% endblock %}
+ {% block head %}
+ <link title="{{ instance_name }}" type="application/opensearchdescription+xml" rel="search" href="{{ url_for('opensearch') }}"/>
+ {% endblock %}
+ </head>
+ <body>
+ <div id="container">
+ {% block content %}
+ {% endblock %}
+ {% if autocomplete %}
+ <script src="{{ url_for('static', filename='js/mootools-core-1.4.5-min.js') }}" ></script>
+ <script src="{{ url_for('static', filename='js/mootools-autocompleter-1.1.2-min.js') }}" ></script>
+ {% endif %}
+ <script type="text/javascript">
+ searx = {};
+ searx.autocompleter = {% if autocomplete %}true{% else %}false{% endif %};
+ </script>
+ <script src="{{ url_for('static', filename='js/searx.js') }}" ></script>
+ </div>
+ </body>
+</html>
diff --git a/searx/templates/default/categories.html b/searx/templates/legacy/categories.html
diff --git a/searx/templates/default/github_ribbon.html b/searx/templates/legacy/github_ribbon.html
diff --git a/searx/templates/legacy/index.html b/searx/templates/legacy/index.html
@@ -0,0 +1,18 @@
+{% extends "legacy/base.html" %}
+{% block content %}
+<div class="center">
+ <div class="title"><h1>searx</h1></div>
+ {% include 'legacy/search.html' %}
+ <p class="top_margin">
+ {% if rtl %}
+ <a href="{{ url_for('preferences') }}" class="hmarg">{{ _('preferences') }}</a>
+ {% endif %}
+ <a href="{{ url_for('about') }}" class="hmarg">{{ _('about') }}</a>
+ {% if not rtl %}
+ <a href="{{ url_for('preferences') }}" class="hmarg">{{ _('preferences') }}</a>
+ {% endif %}
+ </p>
+</div>
+{% include 'legacy/github_ribbon.html' %}
+{% endblock %}
+
diff --git a/searx/templates/default/infobox.html b/searx/templates/legacy/infobox.html
diff --git a/searx/templates/default/opensearch.xml b/searx/templates/legacy/opensearch.xml
diff --git a/searx/templates/default/opensearch_response_rss.xml b/searx/templates/legacy/opensearch_response_rss.xml
diff --git a/searx/templates/legacy/preferences.html b/searx/templates/legacy/preferences.html
@@ -0,0 +1,129 @@
+{% extends "legacy/base.html" %}
+{% block head %} {% endblock %}
+{% block content %}
+<div class="row">
+ <h2>{{ _('Preferences') }}</h2>
+
+ <form method="post" action="{{ url_for('preferences') }}" id="search_form">
+ <fieldset>
+ <legend>{{ _('Default categories') }}</legend>
+ {% set display_tooltip = false %}
+ {% include 'legacy/categories.html' %}
+ </fieldset>
+ <fieldset>
+ <legend>{{ _('Search language') }}</legend>
+ <p>
+ <select name='language'>
+ <option value="all" {% if current_language == 'all' %}selected="selected"{% endif %}>{{ _('Automatic') }}</option>
+ {% for lang_id,lang_name,country_name in language_codes | sort(attribute=1) %}
+ <option value="{{ lang_id }}" {% if lang_id == current_language %}selected="selected"{% endif %}>{{ lang_name }} ({{ country_name }}) - {{ lang_id }}</option>
+ {% endfor %}
+ </select>
+ </p>
+ </fieldset>
+ <fieldset>
+ <legend>{{ _('Interface language') }}</legend>
+ <p>
+ <select name='locale'>
+ {% for locale_id,locale_name in locales.items() | sort %}
+ <option value="{{ locale_id }}" {% if locale_id == current_locale %}selected="selected"{% endif %}>{{ locale_name }}</option>
+ {% endfor %}
+ </select>
+ </p>
+ </fieldset>
+ <fieldset>
+ <legend>{{ _('Autocomplete') }}</legend>
+ <p>
+ <select name="autocomplete">
+ <option value=""> - </option>
+ {% for backend in autocomplete_backends %}
+ <option value="{{ backend }}" {% if backend == autocomplete %}selected="selected"{% endif %}>{{ backend }}</option>
+ {% endfor %}
+ </select>
+ </p>
+ </fieldset>
+ <fieldset>
+ <legend>{{ _('Image proxy') }}</legend>
+ <p>
+ <select name='image_proxy'>
+ <option value="1" {% if image_proxy %}selected="selected"{% endif %}>{{ _('Enabled') }}</option>
+ <option value="" {% if not image_proxy %}selected="selected"{% endif %}>{{ _('Disabled') }}</option>
+ </select>
+ </p>
+ </fieldset>
+ <fieldset>
+ <legend>{{ _('Method') }}</legend>
+ <p>
+ <select name='method'>
+ <option value="POST" {% if method == 'POST' %}selected="selected"{% endif %}>POST</option>
+ <option value="GET" {% if method == 'GET' %}selected="selected"{% endif %}>GET</option>
+ </select>
+ </p>
+ </fieldset>
+ <fieldset>
+ <legend>{{ _('SafeSearch') }}</legend>
+ <p>
+ <select name='safesearch'>
+ <option value="2" {% if safesearch == '2' %}selected="selected"{% endif %}>{{ _('Strict') }}</option>
+ <option value="1" {% if safesearch == '1' %}selected="selected"{% endif %}>{{ _('Moderate') }}</option>
+ <option value="0" {% if safesearch == '0' %}selected="selected"{% endif %}>{{ _('None') }}</option>
+ </select>
+ </p>
+ </fieldset>
+ <fieldset>
+ <legend>{{ _('Themes') }}</legend>
+ <p>
+ <select name="theme">
+ {% for name in themes %}
+ <option value="{{ name }}" {% if name == theme %}selected="selected"{% endif %}>{{ name }}</option>
+ {% endfor %}
+ </select>
+ </p>
+ </fieldset>
+ <fieldset>
+ <legend>{{ _('Results on new tabs') }}</legend>
+ <p>
+ <select name='results_on_new_tab'>
+ <option value="1" {% if results_on_new_tab %}selected="selected"{% endif %}>{{ _('On') }}</option>
+ <option value="0" {% if not results_on_new_tab %}selected="selected"{% endif %}>{{ _('Off')}}</option>
+ </select>
+ </p>
+ </fieldset>
+ <fieldset>
+ <legend>{{ _('Currently used search engines') }}</legend>
+
+ <table>
+ <tr>
+ <th>{{ _('Engine name') }}</th>
+ <th>{{ _('Category') }}</th>
+ <th>{{ _('Allow') }} / {{ _('Block') }}</th>
+ </tr>
+ {% for categ in all_categories %}
+ {% for search_engine in engines_by_category[categ] %}
+
+ {% if not search_engine.private %}
+ <tr>
+ <td>{{ search_engine.name }} ({{ shortcuts[search_engine.name] }})‎</td>
+ <td>{{ _(categ) }}</td>
+ <td class="engine_checkbox">
+ <input type="checkbox" id="engine_{{ categ|replace(' ', '_') }}_{{ search_engine.name|replace(' ', '_') }}" name="engine_{{ search_engine.name }}__{{ categ }}"{% if (search_engine.name, categ) in disabled_engines %} checked="checked"{% endif %} />
+ <label class="allow" for="engine_{{ categ|replace(' ', '_') }}_{{ search_engine.name|replace(' ', '_') }}">{{ _('Allow') }}</label>
+ <label class="deny" for="engine_{{ categ|replace(' ', '_') }}_{{ search_engine.name|replace(' ', '_') }}">{{ _('Block') }}</label>
+ </td>
+ </tr>
+ {% endif %}
+ {% endfor %}
+ {% endfor %}
+ </table>
+ </fieldset>
+ <p class="small_font">{{ _('These settings are stored in your cookies, this allows us not to store this data about you.') }}
+ <br />
+ {{ _("These cookies serve your sole convenience, we don't use these cookies to track you.") }}
+ </p>
+
+ <input type="submit" value="{{ _('save') }}" />
+ <div class="{% if rtl %}left{% else %}right{% endif %} preferences_back"><a href="{{ url_for('clear_cookies') }}">{{ _('Reset defaults') }}</a></div>
+ <div class="{% if rtl %}left{% else %}right{% endif %} preferences_back"><a href="{{ url_for('index') }}">{{ _('back') }}</a></div>
+ </form>
+</div>
+{% endblock %}
diff --git a/searx/templates/default/result_templates/code.html b/searx/templates/legacy/result_templates/code.html
diff --git a/searx/templates/default/result_templates/default.html b/searx/templates/legacy/result_templates/default.html
diff --git a/searx/templates/default/result_templates/images.html b/searx/templates/legacy/result_templates/images.html
diff --git a/searx/templates/default/result_templates/map.html b/searx/templates/legacy/result_templates/map.html
diff --git a/searx/templates/default/result_templates/torrent.html b/searx/templates/legacy/result_templates/torrent.html
diff --git a/searx/templates/default/result_templates/videos.html b/searx/templates/legacy/result_templates/videos.html
diff --git a/searx/templates/legacy/results.html b/searx/templates/legacy/results.html
@@ -0,0 +1,100 @@
+{% extends "legacy/base.html" %}
+{% block title %}{{ q }} - {% endblock %}
+{% block meta %}<link rel="alternate" type="application/rss+xml" title="Searx search: {{ q }}" href="{{ url_for('index') }}?q={{ q|urlencode }}&format=rss&{% for category in selected_categories %}category_{{ category }}=1&{% endfor %}pageno={{ pageno }}">{% endblock %}
+{% block content %}
+<div class="preferences_container right"><a href="{{ url_for('preferences') }}" id="preferences"><span>preferences</span></a></div>
+<div class="small search center">
+ {% include 'legacy/search.html' %}
+</div>
+<div id="results">
+ <div id="sidebar">
+
+ <div id="search_url">
+ {{ _('Search URL') }}:
+ <input type="text" value="{{ base_url }}?q={{ q|urlencode }}{% if selected_categories %}&categories={{ selected_categories|join(",") | replace(' ','+') }}{% endif %}{% if pageno > 1 %}&pageno={{ pageno }}{% endif %}" readonly />
+ </div>
+ <div id="apis">
+ {{ _('Download results') }}
+ {% for output_type in ('csv', 'json', 'rss') %}
+ <form method="{{ method or 'POST' }}" action="{{ url_for('index') }}">
+ <div class="left">
+ <input type="hidden" name="q" value="{{ q }}" />
+ <input type="hidden" name="format" value="{{ output_type }}" />
+ {% for category in selected_categories %}
+ <input type="hidden" name="category_{{ category }}" value="1"/>
+ {% endfor %}
+ <input type="hidden" name="pageno" value="{{ pageno }}" />
+ <input type="submit" value="{{ output_type }}" />
+ </div>
+ </form>
+ {% endfor %}
+ </div>
+ </div>
+
+ {% if answers %}
+ <div id="answers"><span>{{ _('Answers') }}</span>
+ {% for answer in answers %}
+ <span>{{ answer }}</span>
+ {% endfor %}
+ </div>
+ {% endif %}
+
+ {% if suggestions %}
+ <div id="suggestions"><span id="suggestions-title">{{ _('Suggestions') }} : </span>
+ {% set first = true %}
+ {% for suggestion in suggestions %}
+ {% if not first %} • {% endif %}<form method="{{ method or 'POST' }}" action="{{ url_for('index') }}">
+ <input type="hidden" name="q" value="{{ suggestion }}">
+ <input type="submit" class="suggestion" value="{{ suggestion }}" />
+ </form>
+ {% set first = false %}
+ {% endfor %}
+ </div>
+ {% endif %}
+
+ {% if infoboxes %}
+ <div id="infoboxes">
+ {% for infobox in infoboxes %}
+ {% include 'legacy/infobox.html' %}
+ {% endfor %}
+ </div>
+ {% endif %}
+
+ {% for result in results %}
+ {% if result['template'] %}
+ {% include get_result_template('legacy', result['template']) %}
+ {% else %}
+ {% include 'legacy/result_templates/default.html' %}
+ {% endif %}
+ {% endfor %}
+
+ {% if paging %}
+ <div id="pagination">
+ {% if pageno > 1 %}
+ <form method="{{ method or 'POST' }}" action="{{ url_for('index') }}">
+ <div class="{% if rtl %}right{% else %}left{% endif %}">
+ <input type="hidden" name="q" value="{{ q }}" />
+ {% for category in selected_categories %}
+ <input type="hidden" name="category_{{ category }}" value="1"/>
+ {% endfor %}
+ <input type="hidden" name="pageno" value="{{ pageno-1 }}" />
+ <input type="submit" value="<< {{ _('previous page') }}" />
+ </div>
+ </form>
+ {% endif %}
+ <form method="{{ method or 'POST' }}" action="{{ url_for('index') }}">
+ <div class="{% if rtl %}left{% else %}right{% endif %}">
+ {% for category in selected_categories %}
+ <input type="hidden" name="category_{{ category }}" value="1"/>
+ {% endfor %}
+ <input type="hidden" name="q" value="{{ q }}" />
+ <input type="hidden" name="pageno" value="{{ pageno+1 }}" />
+ <input type="submit" value="{{ _('next page') }} >>" />
+ </div>
+ </form>
+
+ <br />
+ </div>
+ {% endif %}
+</div>
+{% endblock %}
diff --git a/searx/templates/legacy/search.html b/searx/templates/legacy/search.html
@@ -0,0 +1,8 @@
+<form method="{{ method or 'POST' }}" action="{{ url_for('index') }}" id="search_form">
+ <div id="search_wrapper">
+ <input type="text" placeholder="{{ _('Search for...') }}" id="q" class="q" name="q" tabindex="1" autocomplete="off" size="100" {% if q %}value="{{ q }}"{% endif %}/>
+ <input type="submit" value="search" id="search_submit" />
+ </div>
+ {% set display_tooltip = true %}
+ {% include 'legacy/categories.html' %}
+</form>
diff --git a/searx/templates/legacy/stats.html b/searx/templates/legacy/stats.html
@@ -0,0 +1,22 @@
+{% extends "legacy/base.html" %}
+{% block head %} {% endblock %}
+{% block content %}
+<h2>{{ _('Engine stats') }}</h2>
+
+{% for stat_name,stat_category in stats %}
+<div class="left">
+ <table>
+ <tr colspan="3">
+ <th>{{ stat_name }}</th>
+ </tr>
+ {% for engine in stat_category %}
+ <tr>
+ <td>{{ engine.name }}</td>
+ <td>{{ '%.02f'|format(engine.avg) }}</td>
+ <td class="percentage"><div style="width: {{ engine.percentage }}%"> </div></td>
+ </tr>
+ {% endfor %}
+ </table>
+</div>
+{% endfor %}
+{% endblock %}
diff --git a/searx/templates/oscar/macros.html b/searx/templates/oscar/macros.html
@@ -33,6 +33,9 @@
<span class="label label-default">{{ engine }}</span>
{% endfor %}
<small>{{ result_link("https://web.archive.org/web/" + result.url, icon('link') + _('cached'), "text-info") }}</small>
+ {% if proxify %}
+ <small>{{ result_link(proxify(result.url), icon('sort') + _('proxied'), "text-info") }}</small>
+ {% endif %}
</div>
<div class="text-muted"><small>{{ result.pretty_url }}</small></div>
{%- endmacro %}
@@ -44,6 +47,9 @@
<span class="label label-default">{{ engine }}</span>
{% endfor %}
<small>{{ result_link("https://web.archive.org/web/" + result.url, icon('link') + _('cached'), "text-info") }}</small>
+ {% if proxify %}
+ <small>{{ result_link(proxify(result.url), icon('sort') + _('proxied'), "text-info") }}</small>
+ {% endif %}
<div class="text-muted"><small>{{ result.pretty_url }}</small></div>
{%- endmacro %}
diff --git a/searx/templates/pix-art/preferences.html b/searx/templates/pix-art/preferences.html
@@ -1,4 +1,4 @@
-{% extends "default/base.html" %}
+{% extends "legacy/base.html" %}
{% block head %} {% endblock %}
{% block content %}
<div class="row">
diff --git a/searx/templates/pix-art/stats.html b/searx/templates/pix-art/stats.html
@@ -1,4 +1,4 @@
-{% extends "default/base.html" %}
+{% extends "legacy/base.html" %}
{% block head %} {% endblock %}
{% block content %}
<h2>{{ _('Engine stats') }}</h2>
diff --git a/searx/utils.py b/searx/utils.py
@@ -252,12 +252,27 @@ def get_torrent_size(filesize, filesize_multiplier):
filesize = int(filesize * 1024 * 1024)
elif filesize_multiplier == 'KB':
filesize = int(filesize * 1024)
+ elif filesize_multiplier == 'TiB':
+ filesize = int(filesize * 1000 * 1000 * 1000 * 1000)
+ elif filesize_multiplier == 'GiB':
+ filesize = int(filesize * 1000 * 1000 * 1000)
+ elif filesize_multiplier == 'MiB':
+ filesize = int(filesize * 1000 * 1000)
+ elif filesize_multiplier == 'KiB':
+ filesize = int(filesize * 1000)
except:
filesize = None
return filesize
+def convert_str_to_int(number_str):
+ if number_str.isdigit():
+ return int(number_str)
+ else:
+ return 0
+
+
def is_valid_lang(lang):
is_abbr = (len(lang) == 2)
if is_abbr:
diff --git a/searx/webapp.py b/searx/webapp.py
@@ -22,10 +22,11 @@ if __name__ == '__main__':
from os.path import realpath, dirname
path.append(realpath(dirname(realpath(__file__)) + '/../'))
-import json
import cStringIO
-import os
import hashlib
+import hmac
+import json
+import os
import requests
from searx import logger
@@ -245,6 +246,20 @@ def url_for_theme(endpoint, override_theme=None, **values):
return url_for(endpoint, **values)
+def proxify(url):
+ if url.startswith('//'):
+ url = 'https:' + url
+
+ if not settings.get('result_proxy'):
+ return url
+
+ h = hmac.new(settings['result_proxy']['key'], url.encode('utf-8'), hashlib.sha256).hexdigest()
+
+ return '{0}?{1}'.format(settings['result_proxy']['url'],
+ urlencode(dict(mortyurl=url.encode('utf-8'),
+ mortyhash=h)))
+
+
def image_proxify(url):
if url.startswith('//'):
@@ -253,8 +268,7 @@ def image_proxify(url):
if not request.preferences.get_value('image_proxy'):
return url
- hash_string = url + settings['server']['secret_key']
- h = hashlib.sha256(hash_string.encode('utf-8')).hexdigest()
+ h = hmac.new(settings['server']['secret_key'], url.encode('utf-8'), hashlib.sha256).hexdigest()
return '{0}?{1}'.format(url_for('image_proxy'),
urlencode(dict(url=url.encode('utf-8'), h=h)))
@@ -313,6 +327,8 @@ def render(template_name, override_theme=None, **kwargs):
kwargs['image_proxify'] = image_proxify
+ kwargs['proxify'] = proxify if settings.get('result_proxy') else None
+
kwargs['get_result_template'] = get_result_template
kwargs['theme'] = get_current_theme_name(override=override_theme)
@@ -602,7 +618,7 @@ def image_proxy():
if not url:
return '', 400
- h = hashlib.sha256(url + settings['server']['secret_key'].encode('utf-8')).hexdigest()
+ h = hmac.new(settings['server']['secret_key'], url, hashlib.sha256).hexdigest()
if h != request.args.get('h'):
return '', 400
@@ -660,6 +676,7 @@ Allow: /
Allow: /about
Disallow: /stats
Disallow: /preferences
+Disallow: /*?*q=*
""", mimetype='text/plain')
diff --git a/tests/robot/test_basic.robot b/tests/robot/test_basic.robot
@@ -4,6 +4,14 @@ Test Setup Open Browser http://localhost:11111/
Test Teardown Close All Browsers
+*** Keywords ***
+Submit Preferences
+ Set Selenium Speed 2 seconds
+ Submit Form id=search_form
+ Location Should Be http://localhost:11111/
+ Set Selenium Speed 0 seconds
+
+
*** Test Cases ***
Front page
Page Should Contain about
@@ -24,8 +32,8 @@ Preferences page
Page Should Contain Preferences
Page Should Contain Default categories
Page Should Contain Currently used search engines
- Page Should Contain dummy_dummy
- Page Should Contain general_dummy
+ Page Should Contain dummy dummy
+ Page Should Contain general dummy
Switch category
Go To http://localhost:11111/preferences
@@ -33,8 +41,7 @@ Switch category
Page Should Contain Checkbox category_dummy
Click Element xpath=//*[.="general"]
Click Element xpath=//*[.="dummy"]
- Submit Form id=search_form
- Location Should Be http://localhost:11111/
+ Submit Preferences
Checkbox Should Not Be Selected category_general
Checkbox Should Be Selected category_dummy
@@ -43,8 +50,7 @@ Change language
Page Should Contain preferences
Go To http://localhost:11111/preferences
Select From List locale hu
- Submit Form id=search_form
- Location Should Be http://localhost:11111/
+ Submit Preferences
Page Should Contain rólunk
Page Should Contain beállítások
@@ -53,13 +59,11 @@ Change method
Page Should Contain preferences
Go To http://localhost:11111/preferences
Select From List method GET
- Submit Form id=search_form
- Location Should Be http://localhost:11111/
+ Submit Preferences
Go To http://localhost:11111/preferences
List Selection Should Be method GET
Select From List method POST
- Submit Form id=search_form
- Location Should Be http://localhost:11111/
+ Submit Preferences
Go To http://localhost:11111/preferences
List Selection Should Be method POST
@@ -67,10 +71,9 @@ Change theme
Page Should Contain about
Page Should Contain preferences
Go To http://localhost:11111/preferences
- List Selection Should Be theme default
+ List Selection Should Be theme legacy
Select From List theme oscar
- Submit Form id=search_form
- Location Should Be http://localhost:11111/
+ Submit Preferences
Go To http://localhost:11111/preferences
List Selection Should Be theme oscar
@@ -80,8 +83,7 @@ Change safesearch
Go To http://localhost:11111/preferences
List Selection Should Be safesearch None
Select From List safesearch Strict
- Submit Form id=search_form
- Location Should Be http://localhost:11111/
+ Submit Preferences
Go To http://localhost:11111/preferences
List Selection Should Be safesearch Strict
@@ -91,8 +93,7 @@ Change image proxy
Go To http://localhost:11111/preferences
List Selection Should Be image_proxy Disabled
Select From List image_proxy Enabled
- Submit Form id=search_form
- Location Should Be http://localhost:11111/
+ Submit Preferences
Go To http://localhost:11111/preferences
List Selection Should Be image_proxy Enabled
@@ -102,8 +103,7 @@ Change search language
Go To http://localhost:11111/preferences
List Selection Should Be language Automatic
Select From List language Turkish (Turkey) - tr_TR
- Submit Form id=search_form
- Location Should Be http://localhost:11111/
+ Submit Preferences
Go To http://localhost:11111/preferences
List Selection Should Be language Turkish (Turkey) - tr_TR
@@ -113,8 +113,7 @@ Change autocomplete
Go To http://localhost:11111/preferences
List Selection Should Be autocomplete -
Select From List autocomplete google
- Submit Form id=search_form
- Location Should Be http://localhost:11111/
+ Submit Preferences
Go To http://localhost:11111/preferences
List Selection Should Be autocomplete google
@@ -126,8 +125,7 @@ Change allowed/disabled engines
Element Should Contain xpath=//label[@class="deny"][@for='engine_dummy_dummy_dummy'] Block
Element Should Contain xpath=//label[@class="deny"][@for='engine_general_general_dummy'] Block
Click Element xpath=//label[@class="deny"][@for='engine_general_general_dummy']
- Submit Form id=search_form
- Location Should Be http://localhost:11111/
+ Submit Preferences
Page Should Contain about
Page Should Contain preferences
Go To http://localhost:11111/preferences
@@ -139,18 +137,16 @@ Block a plugin
Page Should Contain about
Page Should Contain preferences
Go To http://localhost:11111/preferences
- List Selection Should Be theme default
+ List Selection Should Be theme legacy
Select From List theme oscar
- Submit Form id=search_form
- Location Should Be http://localhost:11111/
+ Submit Preferences
Go To http://localhost:11111/preferences
List Selection Should Be theme oscar
Page Should Contain Plugins
Click Link Plugins
Checkbox Should Not Be Selected id=plugin_HTTPS_rewrite
Click Element xpath=//label[@for='plugin_HTTPS_rewrite']
- Submit Form id=search_form
- Location Should Be http://localhost:11111/
+ Submit Preferences
Go To http://localhost:11111/preferences
Page Should Contain Plugins
Click Link Plugins
diff --git a/tests/unit/engines/seedpeer_fixture.html b/tests/unit/engines/seedpeer_fixture.html
@@ -0,0 +1,110 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
+ <html xmlns="http://www.w3.org/1999/xhtml">
+ <head>
+ </head>
+ <body>
+ <div id="header">
+ <div id="whoIsYou">
+ <a href="/lang.php"><small>SeedPeer in your own language?</small></a> <a href="http://www.seedpeer.eu"><img src="/images/flags/uk.gif" width="16px" alt="Torrents EN" /></a> <a href="http://spanish.seedpeer.eu"><img src="/images/flags/es.gif" width="16px" alt="Torrents ES" /></a> <a href="http://german.seedpeer.eu"><img src="/images/flags/de.gif" width="16px" alt="Torrents DE" /></a> <a href="http://french.seedpeer.eu"><img src="/images/flags/fr.gif" width="16px" alt="Torrents FR" /></a> <a href="http://portuguese.seedpeer.eu"><img src="/images/flags/pt.gif" width="16px" alt="Torrents Portuguese" /></a> <a href="http://swedish.seedpeer.eu"><img src="/images/flags/se.gif" width="16px" alt="Torrents Sweden" /></a>
+ </div>
+
+ <script type="text/javascript">
+ whoIsYou();
+ </script>
+ <div id="search">
+ <form action="/search.php" method="get">
+ <input id="topsearchbar" name="search" value="narcos season 2" />
+ <input type="submit" class="searchbutton" value="Torrents" />
+ <input style="color:#000" type="submit" class="searchbutton" name="usenet" value="Usenet Binaries" />
+ </form>
+ <div id="suggestion"></div>
+ </div>
+ <div id="logo"><a href="/"><img src="/images/logo2.gif" alt="Seedpeer homepage" width="415" height="143" /></a></div>
+ <div id="subtext"><a href="/">Home</a> > <a href="/search.html">Torrent search</a> > Narcos season 2 | page 1</div>
+ </div>
+ <div id="nav">
+ <ul>
+ <!--
+ <li><font style="color:red;font-size:9px;font-weight:bold;">NEW</font><a title="Download TOP Games for FREE" rel="nofollow" href="http://www.bigrebelads.com/affiliate/index?ref=9301" target="_blank">FREE Games</a></li>
+
+ -->
+ <li style="border-left:none" id="categories"><a title="Browse Torrent Categories" href="/browse.html">Categories</a>
+ <ul>
+ <li><a title="Browse Anime Torrents" href="/browse.html#6">Anime</a></li>
+ <li><a title="Browse Game Torrents" href="/browse.html#4">Games</a></li>
+ <li><a title="Browse Movie Torrents" href="/browse.html#1">Movies</a></li>
+ <li><a title="Browse Music Torrents" href="/browse.html#3">Music</a></li>
+ <li><a title="Browse Software Torrents" href="/browse.html#5">Software</a></li>
+ <li><a title="Browse TV Torrents" href="/browse.html#2">TV Shows</a></li>
+ <li><a title="Browse Other Torrents" href="/browse.html#7">Others</a></li>
+ </ul>
+ </li>
+ <li><a title="Upload A Torrents" href="/upload.html">Upload torrent</a></li>
+ <li id="verified"><a title="Verified Torrents" href="/verified.html">Verified</a></li>
+ <li id="searchoptions"><a title="Search Torrents" href="/search.html">Torrent search</a></li>
+ <li id="newsgroups"><a style="color:#212b3e" title="News Groups" href="/usenet.html">Usenet Binaries</a></li>
+ <li id="about" style="border-right:none"><a rel="nofollow" href="/faq.html">About Us</a>
+ <ul>
+ <li><a title="SeedPeer Statistics" href="/stats.html">Statistics</a></li>
+ <li><a title="Contact Us" href="/contact.html">Contact</a></li>
+ <li><a title="Frequently Asked Questions" href="/faq.html">FAQ</a></li>
+ <li><a title="SeedPeer API" href="http://api.seedpeer.eu">Our API</a></li>
+ <li><a title="SeedPeer Blog" href="/blog">Blog</a></li>
+ </ul>
+ </li>
+ <!--<li><a href="/toolbar.php">Our Toolbar</a></li>-->
+ </ul>
+ <div class="clear"></div>
+ </div>
+ <div id="body"><div id="pageTop"></div>
+ <div id="headerbox"><h1>Verified <font class="colored">Narcos season 2</font> torrents</h1></div><table width="100%"><tr><th>
+ <span style="float:right">
+ <a href="/search/narcos-season-2/8/1.html"><img style="vertical-align:middle" src="/images/comments.gif" alt="comments" /></a> |
+ <a href="/search/narcos-season-2/7/1.html"><img style="vertical-align:middle" src="/images/ver.gif" alt="verified" /></a>
+ </span>
+ <a href="/search/narcos-season-2/1/1.html">Torrent name</a></th><th class="right"><a href="/search/narcos-season-2/2/1.html">Age</a></th><th class="right"><a href="/search/narcos-season-2/3/1.html">Size</a></th><th class="right"><a href="/search/narcos-season-2/4/1.html">Seeds</a></th><th class="right"><a href="/search/narcos-season-2/5/1.html">Peers</a></th><th class="center"><a href="/search/narcos-season-2/6/1.html">Health</a></th><td class="tableAd" rowspan="6"><iframe src="http://creative.wwwpromoter.com/13689?d=300x250" width="300" height="250" style="border: none;" frameborder="0" scrolling="no"></iframe></td></tr><tr class=""><td><a class="pblink" id="pblink_table_item_1" href="" data-tad="431726" data-last-search="narcos+season+2" target="_blank" rel="nofollow"><strong class='colored'>Narcos season 2</strong> Full Version</a></td><td class="right">20 hours</td><td class="right">681.3 MB</td><td class="right"><font color="green">28</font> </td><td class="right"><font color="navy">654</font> </td><td class="center"><img src="/images/h5.gif" alt="Health" /></td></tr><tr class="tdark"><td><a class="pblink" id="pblink_table_item_2" href="" data-tad="431727" data-url="narcos+season+2" target="_blank" rel="nofollow"><strong class='colored'>Narcos season 2</strong> Trusted Source</a></td><td class="right">12 hours</td><td class="right">787.1 MB</td><td class="right"><font color="green">64</font> </td><td class="right"><font color="navy">220</font> </td><td class="center"><img src="/images/h5.gif" alt="Health" /></td></tr><tr class=""><td><a class="pblink" id="pblink_table_item_3" href="" data-tad="431729" data-last-search="narcos+season+2" target="_blank" rel="nofollow"><strong class='colored'>Full Narcos season 2 Download</strong></a> <small><a class="pblink" id="pblink_table_item_4" href="" data-tad="431729" data-last-search="narcos+season+2" target="_blank" rel="nofollow">Usenet</a></small></td><td class="right">24 hours</td><td class="right">775.5 MB</td><td class="right"><font color="green">60</font> </td><td class="right"><font color="navy">236</font> </td><td class="center"><img src="/images/h5.gif" alt="Health" /></td></tr><tr class="tdark"><td><a class="pblink" id="pblink_table_item_5" href="" data-tad="431730" data-last-search="narcos+season+2" target="_blank" rel="nofollow"><strong class='colored'>Narcos season 2</strong> 2014 - DIRECT STREAMING</a> <small><a class="pblink" id="pblink_table_item_6" href="" data-tad="431729" data-last-search="narcos+season+2" target="_blank" rel="nofollow">Movies</a></small></td><td class="right">17 hours</td><td class="right">654.1 MB</td><td class="right"><font color="green">2</font> </td><td class="right"><font color="navy">391</font> </td><td class="center"><img src="/images/h5.gif" alt="Health" /></td></tr><tr class=""><td><a class="pblink" id="pblink_table_item_7" href="" data-tad="431731" data-last-search="narcos+season+2" target="_blank" rel="nofollow"><strong class='colored'>Narcos season 2</strong> 2014</a> <small><a class="pblink" id="pblink_table_item_8" href="" data-tad="431729" data-last-search="narcos+season+2" target="_blank" rel="nofollow">Movies</a></small></td><td class="right">20 hours</td><td class="right">754.5 MB</td><td class="right"><font color="green">21</font> </td><td class="right"><font color="navy">919</font> </td><td class="center"><img src="/images/h5.gif" alt="Health" /></td></tr></table><br /><br /><center><iframe src='http://creative.wwwpromoter.com/13689?d=728x90' width='728' height='90' style='border: none;' frameborder='0' scrolling='no'></iframe><center><span style="float:right;margin:1em .2em 0 0"><a title="Download at the speed of your connection" href="/usenet.php?search=narcos+season+2"><img src="/images/dlf.gif" alt="Search Binaries" /></a></span><div style="margin-bottom:1em;margin-right:290px" id="headerbox"><h1><a href="/searchfeed/narcos+season+2.xml" target="_blank" title="SeedPeer RSS Torrent Search Feed fornarcos season 2"><img src="/images/feedIcon.png" border="0" /></a> 2 <font class="colored">Narcos season 2</font> Torrents were found</h1></div><table width="100%"><tr><th>
+ <span style="float:right">
+ <a href="/search/narcos-season-2/8/1.html"><img style="vertical-align:middle" src="/images/comments.gif" alt="comments" /></a> |
+ <a href="/search/narcos-season-2/7/1.html"><img style="vertical-align:middle" src="/images/ver.gif" alt="verified" /></a>
+ </span>
+ <a href="/search/narcos-season-2/1/1.html">Torrent name</a></th><th class="right"><a href="/search/narcos-season-2/2/1.html">Age</a></th><th class="right"><a href="/search/narcos-season-2/3/1.html">Size</a></th><th class="right"><a href="/search/narcos-season-2/4/1.html">Seeds</a></th><th class="right"><a href="/search/narcos-season-2/5/1.html">Peers</a></th><th class="center"><a href="/search/narcos-season-2/6/1.html">Health</a></th></tr><tr class=""><td><small class="comments"><a href="http://www.facebook.com/sharer.php?t=Download%20<strong class='colored'>Narcos</strong> <strong class='colored'>Season</strong> <strong class='colored'>2</strong> Complete 7<strong class='colored'>2</strong>0p WebRip EN-SUB x<strong class='colored'>2</strong>64-[MULVAcoded] S0<strong class='colored'>2</strong>%20 torrent&u=http://seedpeer.seedpeer.eu/details/11686840/Narcos-Season-2-Complete-720p-WebRip-EN-SUB-x264-[MULVAcoded]-S02.html"><img src="/images/facebook.png" alt="Add to Facebook" width="14" height="14" /></a></small><a href="/details/11686840/Narcos-Season-2-Complete-720p-WebRip-EN-SUB-x264-[MULVAcoded]-S02.html"><strong class='colored'>Narcos</strong> <strong class='colored'>Season</strong> <strong class='colored'>2</strong> Complete 7<strong class='colored'>2</strong>0p WebRip EN-SUB x<strong class='colored'>2</strong>64-[MULVAcoded] S0<strong class='colored'>2</strong> <small><a href="/browse.html#11686840"></a></small></a></td><td class="right">19 hours</td><td class="right">4.39 GB</td><td class="right"><font color="green">715</font> </td><td class="right"><font color="navy">183</font> </td><td class="center"><img src="/images/h5.gif" alt="Health" width="40" height="11" /></td></tr><tr class="tdark"><td><small class="comments"><a href="http://www.facebook.com/sharer.php?t=Download%20<strong class='colored'>Narcos</strong> - <strong class='colored'>Season</strong> <strong class='colored'>2</strong> - 7<strong class='colored'>2</strong>0p WEBRiP - x<strong class='colored'>2</strong>65 HEVC - ShAaNiG%20 torrent&u=http://seedpeer.seedpeer.eu/details/11685972/Narcos---Season-2---720p-WEBRiP---x265-HEVC---ShAaNiG.html"><img src="/images/facebook.png" alt="Add to Facebook" width="14" height="14" /></a></small><a href="/details/11685972/Narcos---Season-2---720p-WEBRiP---x265-HEVC---ShAaNiG.html"><strong class='colored'>Narcos</strong> - <strong class='colored'>Season</strong> <strong class='colored'>2</strong> - 7<strong class='colored'>2</strong>0p WEBRiP - x<strong class='colored'>2</strong>65 HEVC - ShAaNiG <small><a href="/browse.html#11685972"></a></small></a></td><td class="right">1 day</td><td class="right">2.48 GB</td><td class="right"><font color="green">861</font> </td><td class="right"><font color="navy">332</font> </td><td class="center"><img src="/images/h5.gif" alt="Health" width="40" height="11" /></td></tr></table><div id="headerbox"><h1>Related searches for: <font class="colored">Narcos season 2</font></h1></div><div id="search_suggestions"><br />Other suggested searches: </div><br /><a href="http://torrentz2.eu/search?f=narcos-season-2">Search for "narcos-season-2" on Torrentz2.eu</a><br /><a href="http://torrent-finder.info/show.php?q=narcos-season-2">Search for "narcos-season-2" on Torrent-Finder</a><br /><center><iframe src='http://creative.wwwpromoter.com/13689?d=300x250' width='300' height='250' style='border: none;' frameborder='0' scrolling='no'></iframe> <iframe src='http://creative.wwwpromoter.com/13689?d=300x250' width='300' height='250' style='border: none;' frameborder='0' scrolling='no'></iframe> <iframe src='http://creative.wwwpromoter.com/13689?d=300x250' width='300' height='250' style='border: none;' frameborder='0' scrolling='no'></iframe></center><div id="footer">
+ <table width="100%">
+ <tr>
+ <td width="30%">
+ <h2>Torrents Download</h2>
+ <a href="/">Torrent search</a><br />
+ <a href="/browse.html">Browse categories</a><br />
+ <a href="/verified.html">Verified Torrents</a><br />
+ <a href="/order-date.html">Today's torrents</a><br />
+ <a href="/yesterday.html">Yesterday's torrents</a><br />
+ <a href="/stats.html">Statistics</a><br />
+ <br />
+ <a href="/faq.html#copyright"><strong>Copyright & Removal</strong></a>
+ </td>
+ <td width="30%"><h2>Cool Stuff</h2>
+ <a href="/promotional.php">Promotional</a><br />
+ <a href="/contact.html">Advertising Information</a><br />
+ <strong><a href="/plugins.php" title="Add a search plugin to Firefox or Internet Explorer">Search Plugin <span style="color:red">*</span></a></strong><br />
+ <a href="http://www.utorrent.com">µTorrent Client</a><br />
+ <a href="/blog">Seedpeer Blog</a><br />
+ </td>
+ <td width="30%"><h2>Links</h2>
+ <a href="http://www.sumotorrent.com" target="_blank"><strong>SumoTorrent</strong></a><br />
+ <a href="http://www.torrent-finder.info" target="_blank"><strong>Torrent Finder</strong></a><br />
+ <a href="http://www.torrentpond.com" target="_blank"><strong>TorrentPond</strong></a><br />
+ <a href="https://www.limetorrents.cc" target="_blank">LimeTorrents.cc</a><br />
+ <a href="http://www.torrents.to/" target="_blank">Torrents.to</a><br />
+ <a href="http://www.torrentfunk.com" target="_blank">TorrentFunk</a><br />
+ <a href="https://monova.org" target="_blank">Monova</a><br />
+ <a href="http://www.torrentroom.com" target="_blank">TorrentRoom</a><br />
+ <a href="http://www.katcr.co/" target="_blank">Kickass Torrents Community</a><br />
+ </td>
+ <td width="10%"><div id="bottomlogo"></div></td>
+ </tr>
+ </table>
+ <br />
+ <br />
+ </div>
+ </div>
+ </body>
+ </html>
+\ No newline at end of file
diff --git a/tests/unit/engines/test_digbt.py b/tests/unit/engines/test_digbt.py
@@ -28,7 +28,9 @@ class TestDigBTEngine(SearxTestCase):
<table class="table">
<tr><td class="x-item">
<div>
- <a title="The Big Bang Theory" class="title" href="/The-Big-Bang-Theory-d2.html">The Big Bang Theory</a>
+ <a title="The Big Bang Theory" class="title" href="/The-Big-Bang-Theory-d2.html">
+ The Big <span class="highlight">Bang</span> Theory
+ </a>
<span class="ctime"><span style="color:red;">4 hours ago</span></span>
</div>
<div class="files">
diff --git a/tests/unit/engines/test_kickass.py b/tests/unit/engines/test_kickass.py
@@ -14,7 +14,7 @@ class TestKickassEngine(SearxTestCase):
params = kickass.request(query, dicto)
self.assertIn('url', params)
self.assertIn(query, params['url'])
- self.assertIn('kickass.to', params['url'])
+ self.assertIn('kickass.cd', params['url'])
self.assertFalse(params['verify'])
def test_response(self):
@@ -84,7 +84,7 @@ class TestKickassEngine(SearxTestCase):
</span>
</div>
</td>
- <td class="nobr center">449 <span>bytes</span></td>
+ <td class="nobr center">449 bytes</td>
<td class="center">4</td>
<td class="center">2 years</td>
<td class="green center">10</td>
@@ -97,7 +97,7 @@ class TestKickassEngine(SearxTestCase):
self.assertEqual(type(results), list)
self.assertEqual(len(results), 1)
self.assertEqual(results[0]['title'], 'This should be the title')
- self.assertEqual(results[0]['url'], 'https://kickass.to/url.html')
+ self.assertEqual(results[0]['url'], 'https://kickass.cd/url.html')
self.assertEqual(results[0]['content'], 'Posted by riri in Other > Unsorted')
self.assertEqual(results[0]['seed'], 10)
self.assertEqual(results[0]['leech'], 1)
@@ -191,7 +191,7 @@ class TestKickassEngine(SearxTestCase):
</span>
</div>
</td>
- <td class="nobr center">1 <span>KB</span></td>
+ <td class="nobr center">1 KiB</td>
<td class="center">4</td>
<td class="center">2 years</td>
<td class="green center">10</td>
@@ -235,7 +235,7 @@ class TestKickassEngine(SearxTestCase):
</span>
</div>
</td>
- <td class="nobr center">1 <span>MB</span></td>
+ <td class="nobr center">1 MiB</td>
<td class="center">4</td>
<td class="center">2 years</td>
<td class="green center">9</td>
@@ -279,7 +279,7 @@ class TestKickassEngine(SearxTestCase):
</span>
</div>
</td>
- <td class="nobr center">1 <span>GB</span></td>
+ <td class="nobr center">1 GiB</td>
<td class="center">4</td>
<td class="center">2 years</td>
<td class="green center">8</td>
@@ -323,7 +323,7 @@ class TestKickassEngine(SearxTestCase):
</span>
</div>
</td>
- <td class="nobr center">1 <span>TB</span></td>
+ <td class="nobr center">1 TiB</td>
<td class="center">4</td>
<td class="center">2 years</td>
<td class="green center">7</td>
@@ -367,7 +367,7 @@ class TestKickassEngine(SearxTestCase):
</span>
</div>
</td>
- <td class="nobr center">z <span>bytes</span></td>
+ <td class="nobr center">z bytes</td>
<td class="center">r</td>
<td class="center">2 years</td>
<td class="green center">a</td>
@@ -380,17 +380,17 @@ class TestKickassEngine(SearxTestCase):
self.assertEqual(type(results), list)
self.assertEqual(len(results), 5)
self.assertEqual(results[0]['title'], 'This should be the title')
- self.assertEqual(results[0]['url'], 'https://kickass.to/url.html')
+ self.assertEqual(results[0]['url'], 'https://kickass.cd/url.html')
self.assertEqual(results[0]['content'], 'Posted by riri in Other > Unsorted')
self.assertEqual(results[0]['seed'], 10)
self.assertEqual(results[0]['leech'], 1)
self.assertEqual(results[0]['files'], 4)
self.assertEqual(results[0]['magnetlink'], 'magnet:?xt=urn:btih:MAGNETURL&dn=test')
self.assertEqual(results[0]['torrentfile'], 'http://torcache.net/torrent/53917.torrent?title=test')
- self.assertEqual(results[0]['filesize'], 1024)
- self.assertEqual(results[1]['filesize'], 1048576)
- self.assertEqual(results[2]['filesize'], 1073741824)
- self.assertEqual(results[3]['filesize'], 1099511627776)
+ self.assertEqual(results[0]['filesize'], 1000)
+ self.assertEqual(results[1]['filesize'], 1000000)
+ self.assertEqual(results[2]['filesize'], 1000000000)
+ self.assertEqual(results[3]['filesize'], 1000000000000)
self.assertEqual(results[4]['seed'], 0)
self.assertEqual(results[4]['leech'], 0)
self.assertEqual(results[4]['files'], None)
diff --git a/tests/unit/engines/test_pdbe.py b/tests/unit/engines/test_pdbe.py
@@ -0,0 +1,109 @@
+import mock
+from collections import defaultdict
+from searx.engines import pdbe
+from searx.testing import SearxTestCase
+
+
+class TestPdbeEngine(SearxTestCase):
+ def test_request(self):
+ query = 'test_query'
+ dicto = defaultdict(dict)
+ params = pdbe.request(query, dicto)
+ self.assertTrue('url' in params)
+ self.assertTrue('ebi.ac.uk' in params['url'])
+ self.assertTrue('data' in params)
+ self.assertTrue('q' in params['data'])
+ self.assertTrue(query in params['data']['q'])
+ self.assertTrue('wt' in params['data'])
+ self.assertTrue('json' in params['data']['wt'])
+ self.assertTrue('method' in params)
+ self.assertTrue(params['method'] == 'POST')
+
+ def test_response(self):
+ self.assertRaises(AttributeError, pdbe.response, None)
+ self.assertRaises(AttributeError, pdbe.response, [])
+ self.assertRaises(AttributeError, pdbe.response, '')
+ self.assertRaises(AttributeError, pdbe.response, '[]')
+
+ json = """
+{
+ "response": {
+ "docs": [
+ {
+ "citation_title": "X-ray crystal structure of ferric Aplysia limacina myoglobin in different liganded states.",
+ "citation_year": 1993,
+ "entry_author_list": [
+ "Conti E, Moser C, Rizzi M, Mattevi A, Lionetti C, Coda A, Ascenzi P, Brunori M, Bolognesi M"
+ ],
+ "journal": "J. Mol. Biol.",
+ "journal_page": "498-508",
+ "journal_volume": "233",
+ "pdb_id": "2fal",
+ "status": "REL",
+ "title": "X-RAY CRYSTAL STRUCTURE OF FERRIC APLYSIA LIMACINA MYOGLOBIN IN DIFFERENT LIGANDED STATES"
+ }
+ ],
+ "numFound": 1,
+ "start": 0
+ },
+ "responseHeader": {
+ "QTime": 0,
+ "params": {
+ "q": "2fal",
+ "wt": "json"
+ },
+ "status": 0
+ }
+}
+"""
+
+ response = mock.Mock(text=json)
+ results = pdbe.response(response)
+ self.assertEqual(type(results), list)
+ self.assertEqual(len(results), 1)
+ self.assertEqual(results[0]['title'],
+ 'X-RAY CRYSTAL STRUCTURE OF FERRIC APLYSIA LIMACINA MYOGLOBIN IN DIFFERENT LIGANDED STATES')
+ self.assertEqual(results[0]['url'], pdbe.pdbe_entry_url.format(pdb_id='2fal'))
+ self.assertEqual(results[0]['img_src'], pdbe.pdbe_preview_url.format(pdb_id='2fal'))
+ self.assertTrue('Conti E' in results[0]['content'])
+ self.assertTrue('X-ray crystal structure of ferric Aplysia limacina myoglobin in different liganded states.' in
+ results[0]['content'])
+ self.assertTrue('1993' in results[0]['content'])
+
+ # Testing proper handling of PDB entries marked as obsolete
+ json = """
+{
+ "response": {
+ "docs": [
+ {
+ "citation_title": "Obsolete entry test",
+ "citation_year": 2016,
+ "entry_author_list": ["Doe J"],
+ "journal": "J. Obs.",
+ "journal_page": "1-2",
+ "journal_volume": "1",
+ "pdb_id": "xxxx",
+ "status": "OBS",
+ "title": "OBSOLETE ENTRY TEST",
+ "superseded_by": "yyyy"
+ }
+ ],
+ "numFound": 1,
+ "start": 0
+ },
+ "responseHeader": {
+ "QTime": 0,
+ "params": {
+ "q": "xxxx",
+ "wt": "json"
+ },
+ "status": 0
+ }
+}
+"""
+ response = mock.Mock(text=json)
+ results = pdbe.response(response)
+ self.assertEqual(type(results), list)
+ self.assertEqual(len(results), 1)
+ self.assertEqual(results[0]['title'], 'OBSOLETE ENTRY TEST (OBSOLETE)')
+ self.assertTrue(results[0]['content'].startswith('<em>This entry has been superseded by'))
diff --git a/tests/unit/engines/test_seedpeer.py b/tests/unit/engines/test_seedpeer.py
@@ -0,0 +1,51 @@
+import mock
+from collections import defaultdict
+from searx.engines import seedpeer
+from searx.testing import SearxTestCase
+from datetime import datetime
+
+
+class TestSeedPeerEngine(SearxTestCase):
+
+ html = ''
+ with open('./tests/unit/engines/seedpeer_fixture.html') as fixture:
+ html += fixture.read()
+
+ def test_request(self):
+ query = 'test_query'
+ dicto = defaultdict(dict)
+ dicto['pageno'] = 1
+ params = seedpeer.request(query, dicto)
+ self.assertIn('url', params)
+ self.assertIn(query, params['url'])
+ self.assertIn('seedpeer.eu', params['url'])
+
+ def test_response_raises_attr_error_on_empty_response(self):
+ self.assertRaises(AttributeError, seedpeer.response, None)
+ self.assertRaises(AttributeError, seedpeer.response, [])
+ self.assertRaises(AttributeError, seedpeer.response, '')
+ self.assertRaises(AttributeError, seedpeer.response, '[]')
+
+ def test_response_returns_empty_list(self):
+ response = mock.Mock(text='<html></html>')
+ self.assertEqual(seedpeer.response(response), [])
+
+ def test_response_returns_all_results(self):
+ response = mock.Mock(text=self.html)
+ results = seedpeer.response(response)
+ self.assertTrue(isinstance(results, list))
+ self.assertEqual(len(results), 2)
+
+ def test_response_returns_correct_results(self):
+ response = mock.Mock(text=self.html)
+ results = seedpeer.response(response)
+ self.assertEqual(
+ results[0]['title'], 'Narcos - Season 2 - 720p WEBRiP - x265 HEVC - ShAaNiG '
+ )
+ self.assertEqual(
+ results[0]['url'],
+ 'http://www.seedpeer.eu/details/11685972/Narcos---Season-2---720p-WEBRiP---x265-HEVC---ShAaNiG.html'
+ )
+ self.assertEqual(results[0]['content'], '2.48 GB, 1 day')
+ self.assertEqual(results[0]['seed'], '861')
+ self.assertEqual(results[0]['leech'], '332')
diff --git a/tests/unit/test_webapp.py b/tests/unit/test_webapp.py
@@ -44,7 +44,7 @@ class ViewsTestCase(SearxTestCase):
webapp.Search.search = search_mock
def get_current_theme_name_mock(override=None):
- return 'default'
+ return 'legacy'
webapp.get_current_theme_name = get_current_theme_name_mock
@@ -58,7 +58,7 @@ class ViewsTestCase(SearxTestCase):
def test_index_html(self):
result = self.app.post('/', data={'q': 'test'})
self.assertIn(
- '<h3 class="result_title"><img width="14" height="14" class="favicon" src="/static/themes/default/img/icons/icon_youtube.ico" alt="youtube" /><a href="http://second.test.xyz" rel="noreferrer">Second <span class="highlight">Test</span></a></h3>', # noqa
+ '<h3 class="result_title"><img width="14" height="14" class="favicon" src="/static/themes/legacy/img/icons/icon_youtube.ico" alt="youtube" /><a href="http://second.test.xyz" rel="noreferrer">Second <span class="highlight">Test</span></a></h3>', # noqa
result.data
)
self.assertIn(