on master help sign in
Merge branch 'master' of https://github.com/asciimoo/searx
commitefb6eca39e9dd851645f76bd72ac076f61a11c09 authorpw3t <romain@berthor.fr> dateThu 2 Jan 2014, 19:52 (13 years ago) parent6f2b8acd2898b0
15 files changed+131−26
README.md+1−1 engines.cfg_sample+5 searx/engines/dailymotion.py+32 searx/engines/flickr.py+1−1 searx/engines/piratebay.py+1−1 searx/engines/xpath.py+1−1 searx/settings.py+1−1 searx/static/js/searx.js+27 searx/templates/base.html+1 searx/templates/categories.html+5 searx/templates/index.html+1 searx/templates/preferences.html+19 searx/templates/results.html+4−3 searx/templates/search.html+1−5 searx/webapp.py+31−13
README.md+1−1
@@ -13,6 +13,7 @@ List of [running instances](https://github.com/asciimoo/searx/wiki/Searx-instanc
1313* Modular (see [examples](https://github.com/asciimoo/searx/blob/master/examples))
1414* Parallel queries
1515* Supports json output `curl https://searx.0x2a.tk/?format=json&q=[query]`
16+* Supports csv output `curl https://searx.0x2a.tk/?format=csv&q=[query]`
1617* Opensearch support (you can set as default search engine)
1718* Configurable search engines/categories
1819* User-agent forwarding
@@ -32,7 +33,6 @@ List of [running instances](https://github.com/asciimoo/searx/wiki/Searx-instanc
3233* Language support
3334* Documentation
3435* Pagination
35−* Search suggestions
3636* Tests
3737
3838
engines.cfg_sample+5
@@ -79,3 +79,8 @@ suggestion_xpath = //div[@id="satat"]//a
7979[youtube]
8080engine = youtube
8181categories = videos
82+
83+[dailymotion]
84+engine = dailymotion
85+categories = videos
86+
searx/engines/dailymotion.pynew file+32
@@ -0,0 +1,32 @@
1+from urllib import urlencode
2+from json import loads
3+from cgi import escape
4+
5+categories = ['videos']
6+localization = 'en'
7+
8+# see http://www.dailymotion.com/doc/api/obj-video.html
9+search_url = 'https://api.dailymotion.com/videos?fields=title,description,duration,url,thumbnail_360_url&sort=relevance&limit=25&page=1&{query}'
10+
11+def request(query, params):
12+ global search_url
13+ params['url'] = search_url.format(query=urlencode({'search': query, 'localization': localization }))
14+ return params
15+
16+
17+def response(resp):
18+ results = []
19+ search_res = loads(resp.text)
20+ if not 'list' in search_res:
21+ return results
22+ for res in search_res['list']:
23+ title = res['title']
24+ url = res['url']
25+ if res['thumbnail_360_url']:
26+ content = '<a href="{0}" title="{0}" ><img src="{1}" /></a><br />'.format(url, res['thumbnail_360_url'])
27+ else:
28+ content = ''
29+ if res['description']:
30+ content += escape(res['description'][:500])
31+ results.append({'url': url, 'title': title, 'content': content})
32+ return results
searx/engines/flickr.py+1−1
@@ -7,7 +7,7 @@ from urlparse import urljoin
77categories = ['images']
88
99url = 'https://secure.flickr.com/'
10−search_url = url+'search/?q={query}'
10+search_url = url+'search/?{query}'
1111
1212def request(query, params):
1313 params['url'] = search_url.format(query=urlencode({'q': query}))
searx/engines/piratebay.py+1−1
@@ -5,7 +5,7 @@ from urllib import quote
55
66categories = ['videos', 'music']
77
8−url = 'https://thepiratebay.sx/'
8+url = 'https://thepiratebay.se/'
99search_url = url + 'search/{search_term}/0/99/{search_type}'
1010search_types = {'videos': '200'
1111 ,'music' : '100'
searx/engines/xpath.py+1−1
@@ -28,7 +28,7 @@ def extract_url(xpath_results):
2828 url = xpath_results[0].attrib.get('href')
2929 else:
3030 url = xpath_results.attrib.get('href')
31− if not url.startswith('http://') or not url.startswith('https://'):
31+ if not url.startswith('http://') and not url.startswith('https://'):
3232 url = 'http://'+url
3333 parsed_url = urlparse(url)
3434 if not parsed_url.netloc:
searx/settings.py+1−1
@@ -13,4 +13,4 @@ blacklist = [] # search engine blacklist
1313
1414categories = {} # custom search engine categories
1515
16−hostname = None # domain name or None - if you want to rewrite the default HTTP host
16+base_url = None # "https://your.domain.tld/" or None (to use request parameters)
searx/static/js/searx.jsnew file+27
@@ -0,0 +1,27 @@
1+(function (w, d) {
2+ 'use strict';
3+ function addListener(el, type, fn) {
4+ if (el.addEventListener) {
5+ el.addEventListener(type, fn, false);
6+ } else {
7+ el.attachEvent('on' + type, fn);
8+ }
9+ }
10+
11+ function placeCursorAtEnd() {
12+ if (this.setSelectionRange) {
13+ var len = this.value.length * 2;
14+ this.setSelectionRange(len, len);
15+ }
16+ }
17+
18+ addListener(w, 'load', function () {
19+ var qinput = d.getElementById('q');
20+ if (qinput !== null) {
21+ addListener(qinput, 'focus', placeCursorAtEnd);
22+ qinput.focus();
23+ }
24+ });
25+
26+})(window, document);
27+
searx/templates/base.html+1
@@ -18,6 +18,7 @@
1818<div id="container">
1919{% block content %}
2020{% endblock %}
21+<script src="/static/js/searx.js" ></script>
2122</div>
2223</body>
2324</html>
searx/templates/categories.htmlnew file+5
@@ -0,0 +1,5 @@
1+{% for category in categories %}
2+ <div class="checkbox_container">
3+ <input type="checkbox" id="checkbox_{{ category|replace(' ', '_') }}" name="category_{{ category }}" {% if category in selected_categories %}checked="checked"{% endif %} /><label for="checkbox_{{ category|replace(' ', '_') }}" class="cb"></label><label for="checkbox_{{ category|replace(' ', '_') }}">{{ category }}</label>
4+ </div>
5+{% endfor %}
searx/templates/index.html+1
@@ -6,6 +6,7 @@
66 {% include 'search.html' %}
77 <p class="top_margin">
88 <a href="/about" class="hmarg">about</a>
9+ <a href="/preferences" class="hmarg">preferences</a>
910 </p>
1011</div>
1112{% endblock %}
searx/templates/preferences.htmlnew file+19
@@ -0,0 +1,19 @@
1+{% extends "base.html" %}
2+{% block head %} {% endblock %}
3+{% block content %}
4+<div class="row">
5+ <h2>Preferences</h2>
6+
7+
8+ <fieldset>
9+ <legend>Default categories</legend>
10+ <form method="post" action="/preferences" id="search_form">
11+ <p>
12+ {% include 'categories.html' %}
13+ </p>
14+ <input type="submit" value="save" />
15+ </form>
16+ </fieldset>
17+ <div class="right"><a href="/">back</a></div>
18+</div>
19+{% endblock %}
searx/templates/results.html+4−3
@@ -1,12 +1,13 @@
11{% extends "base.html" %}
22{% block title %}{{ q }} - {% endblock %}
33{% block content %}
4+<div class="right"><a href="/preferences">preferences</a></div>
45<div class="small">
56 {% include 'search.html' %}
67</div>
78<div id="results">
89 {% if suggestions %}
9− <div id="suggestions">Suggestions: {% for suggestion in suggestions %}<form method="post" action=""><input type="hidden" name="q" value="{{suggestion}}"><input type="submit" value="{{ suggestion }}" /></form>{% endfor %}</div>
10+ <div id="suggestions">Suggestions: {% for suggestion in suggestions %}<form method="post" action="/"><input type="hidden" name="q" value="{{suggestion}}"><input type="submit" value="{{ suggestion }}" /></form>{% endfor %}</div>
1011 {% endif %}
1112 <div>
1213 Number of results: {{ number_of_results }}
@@ -18,14 +19,14 @@
1819 {% include 'result_templates/default.html' %}
1920 {% endif %}
2021 {% endfor %}
21− <form method="post" action="">
22+ <form method="post" action="/">
2223 <div class="left">
2324 <input type="hidden" name="q" value="{{ q }}" />
2425 <input type="hidden" name="format" value="csv" />
2526 <input type="submit" value="download results in csv" />
2627 </div>
2728 </form>
28− <form method="post" action="">
29+ <form method="post" action="/">
2930 <div class="">
3031 <input type="hidden" name="q" value="{{ q }}" />
3132 <input type="hidden" name="format" value="json" />
searx/templates/search.html+1−5
@@ -4,10 +4,6 @@
44 <input type="submit" value="" id="search_submit" />
55</div>
66 <div>
7− {% for category in categories %}
8− <div class="checkbox_container">
9− <input type="checkbox" id="checkbox_{{ category|replace(' ', '_') }}" name="category_{{ category }}" {% if category in selected_categories %}checked="checked"{% endif %} /><label for="checkbox_{{ category|replace(' ', '_') }}" class="cb"></label><label for="checkbox_{{ category|replace(' ', '_') }}">{{ category }}</label>
10− </div>
11− {% endfor %}
7+ {% include 'categories.html' %}
128 </div>
139</form>
searx/webapp.py+31−13
@@ -22,7 +22,7 @@ if __name__ == "__main__":
2222 from sys import path
2323 path.append(os.path.realpath(os.path.dirname(os.path.realpath(__file__))+'/../'))
2424
25−from flask import Flask, request, render_template, url_for, Response, make_response
25+from flask import Flask, request, render_template, url_for, Response, make_response, redirect
2626from searx.engines import search, categories, engines, get_engines_stats
2727from searx import settings
2828import json
@@ -124,29 +124,46 @@ def index():
124124 response.headers.add('Content-Disposition', 'attachment;Filename=searx_-_{0}.csv'.format('_'.join(query.split())))
125125 return response
126126
127− template = render('results.html'
128− ,results=results
129− ,q=request_data['q']
130− ,selected_categories=selected_categories
131− ,number_of_results=len(results)
132− ,suggestions=suggestions
133− )
134− resp = make_response(template)
135− resp.set_cookie('categories', ','.join(selected_categories))
127+ return render('results.html'
128+ ,results=results
129+ ,q=request_data['q']
130+ ,selected_categories=selected_categories
131+ ,number_of_results=len(results)
132+ ,suggestions=suggestions
133+ )
136134
137− return resp
138135
139136@app.route('/about', methods=['GET'])
140137def about():
141138 global categories
142139 return render('about.html', categs=categories.items())
143140
141+
142+@app.route('/preferences', methods=['GET', 'POST'])
143+def preferences():
144+
145+ if request.method=='POST':
146+ selected_categories = []
147+ for pd_name,pd in request.form.items():
148+ if pd_name.startswith('category_'):
149+ category = pd_name[9:]
150+ if not category in categories:
151+ continue
152+ selected_categories.append(category)
153+ if selected_categories:
154+ resp = make_response(redirect('/'))
155+ resp.set_cookie('categories', ','.join(selected_categories))
156+ return resp
157+ return render('preferences.html')
158+
159+
144160@app.route('/stats', methods=['GET'])
145161def stats():
146162 global categories
147163 stats = get_engines_stats()
148164 return render('stats.html', stats=stats)
149165
166+
150167@app.route('/robots.txt', methods=['GET'])
151168def robots():
152169 return Response("""User-agent: *
@@ -155,6 +172,7 @@ Allow: /about
155172Disallow: /stats
156173""", mimetype='text/plain')
157174
175+
158176@app.route('/opensearch.xml', methods=['GET'])
159177def opensearch():
160178 global opensearch_xml
@@ -165,8 +183,8 @@ def opensearch():
165183 method = 'get'
166184 if request.is_secure:
167185 scheme = 'https'
168− if settings.hostname:
169− hostname = '{0}://{1}/'.format(scheme,settings.hostname)
186+ if settings.base_url:
187+ hostname = settings.base_url
170188 else:
171189 hostname = url_for('index', _external=True, _scheme=scheme)
172190 ret = opensearch_xml.format(method=method, host=hostname)