Browse Source

Merge branch 'master' of https://github.com/asciimoo/searx

pw3t 11 years ago
parent
commit
efb6eca39e

+ 1
- 1
README.md View File

@@ -13,6 +13,7 @@ List of [running instances](https://github.com/asciimoo/searx/wiki/Searx-instanc
13 13
 * Modular (see [examples](https://github.com/asciimoo/searx/blob/master/examples))
14 14
 * Parallel queries
15 15
 * 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]`
16 17
 * Opensearch support (you can set as default search engine)
17 18
 * Configurable search engines/categories
18 19
 * User-agent forwarding
@@ -32,7 +33,6 @@ List of [running instances](https://github.com/asciimoo/searx/wiki/Searx-instanc
32 33
 * Language support
33 34
 * Documentation
34 35
 * Pagination
35
-* Search suggestions
36 36
 * Tests
37 37
 
38 38
 

+ 5
- 0
engines.cfg_sample View File

@@ -79,3 +79,8 @@ suggestion_xpath = //div[@id="satat"]//a
79 79
 [youtube]
80 80
 engine = youtube
81 81
 categories = videos
82
+
83
+[dailymotion]
84
+engine = dailymotion
85
+categories = videos
86
+

+ 32
- 0
searx/engines/dailymotion.py View File

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

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

@@ -7,7 +7,7 @@ from urlparse import urljoin
7 7
 categories = ['images']
8 8
 
9 9
 url = 'https://secure.flickr.com/'
10
-search_url = url+'search/?q={query}'
10
+search_url = url+'search/?{query}'
11 11
 
12 12
 def request(query, params):
13 13
     params['url'] = search_url.format(query=urlencode({'q': query}))

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

@@ -5,7 +5,7 @@ from urllib import quote
5 5
 
6 6
 categories = ['videos', 'music']
7 7
 
8
-url = 'https://thepiratebay.sx/'
8
+url = 'https://thepiratebay.se/'
9 9
 search_url = url + 'search/{search_term}/0/99/{search_type}'
10 10
 search_types = {'videos': '200'
11 11
                ,'music' : '100'

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

@@ -28,7 +28,7 @@ def extract_url(xpath_results):
28 28
             url = xpath_results[0].attrib.get('href')
29 29
     else:
30 30
         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://'):
32 32
         url = 'http://'+url
33 33
     parsed_url = urlparse(url)
34 34
     if not parsed_url.netloc:

+ 1
- 1
searx/settings.py View File

@@ -13,4 +13,4 @@ blacklist = [] # search engine blacklist
13 13
 
14 14
 categories = {} # custom search engine categories
15 15
 
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)

+ 27
- 0
searx/static/js/searx.js View File

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

+ 1
- 0
searx/templates/base.html View File

@@ -18,6 +18,7 @@
18 18
 <div id="container">
19 19
 {% block content %}
20 20
 {% endblock %}
21
+<script src="/static/js/searx.js" ></script>
21 22
 </div>
22 23
 </body>
23 24
 </html>

+ 5
- 0
searx/templates/categories.html View File

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

+ 1
- 0
searx/templates/index.html View File

@@ -6,6 +6,7 @@
6 6
     {% include 'search.html' %}
7 7
     <p class="top_margin">
8 8
         <a href="/about" class="hmarg">about</a>
9
+        <a href="/preferences" class="hmarg">preferences</a>
9 10
     </p>
10 11
 </div>
11 12
 {% endblock %}

+ 19
- 0
searx/templates/preferences.html View File

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

+ 4
- 3
searx/templates/results.html View File

@@ -1,12 +1,13 @@
1 1
 {% extends "base.html" %}
2 2
 {% block title %}{{ q }} - {% endblock %}
3 3
 {% block content %}
4
+<div class="right"><a href="/preferences">preferences</a></div>
4 5
 <div class="small">
5 6
     {% include 'search.html' %}
6 7
 </div>
7 8
 <div id="results">
8 9
     {% 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>
10 11
     {% endif %}
11 12
     <div>
12 13
         Number of results: {{ number_of_results }}
@@ -18,14 +19,14 @@
18 19
             {% include 'result_templates/default.html' %}
19 20
         {% endif %}
20 21
     {% endfor %}
21
-    <form method="post" action="">
22
+    <form method="post" action="/">
22 23
         <div class="left">
23 24
             <input type="hidden" name="q" value="{{ q }}" />
24 25
             <input type="hidden" name="format" value="csv" />
25 26
             <input type="submit" value="download results in csv" />
26 27
         </div>
27 28
     </form>
28
-    <form method="post" action="">
29
+    <form method="post" action="/">
29 30
         <div class="">
30 31
             <input type="hidden" name="q" value="{{ q }}" />
31 32
             <input type="hidden" name="format" value="json" />

+ 1
- 5
searx/templates/search.html View File

@@ -4,10 +4,6 @@
4 4
     <input type="submit" value="" id="search_submit" />
5 5
 </div>
6 6
     <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' %}
12 8
     </div>
13 9
 </form>

+ 31
- 13
searx/webapp.py View File

@@ -22,7 +22,7 @@ if __name__ == "__main__":
22 22
     from sys import path
23 23
     path.append(os.path.realpath(os.path.dirname(os.path.realpath(__file__))+'/../'))
24 24
 
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
26 26
 from searx.engines import search, categories, engines, get_engines_stats
27 27
 from searx import settings
28 28
 import json
@@ -124,29 +124,46 @@ def index():
124 124
         response.headers.add('Content-Disposition', 'attachment;Filename=searx_-_{0}.csv'.format('_'.join(query.split())))
125 125
         return response
126 126
 
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
+                 )
136 134
 
137
-    return resp
138 135
 
139 136
 @app.route('/about', methods=['GET'])
140 137
 def about():
141 138
     global categories
142 139
     return render('about.html', categs=categories.items())
143 140
 
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
+
144 160
 @app.route('/stats', methods=['GET'])
145 161
 def stats():
146 162
     global categories
147 163
     stats = get_engines_stats()
148 164
     return render('stats.html', stats=stats)
149 165
 
166
+
150 167
 @app.route('/robots.txt', methods=['GET'])
151 168
 def robots():
152 169
     return Response("""User-agent: *
@@ -155,6 +172,7 @@ Allow: /about
155 172
 Disallow: /stats
156 173
 """, mimetype='text/plain')
157 174
 
175
+
158 176
 @app.route('/opensearch.xml', methods=['GET'])
159 177
 def opensearch():
160 178
     global opensearch_xml
@@ -165,8 +183,8 @@ def opensearch():
165 183
         method = 'get'
166 184
     if request.is_secure:
167 185
         scheme = 'https'
168
-    if settings.hostname:
169
-        hostname = '{0}://{1}/'.format(scheme,settings.hostname)
186
+    if settings.base_url:
187
+        hostname = settings.base_url
170 188
     else:
171 189
         hostname = url_for('index', _external=True, _scheme=scheme)
172 190
     ret = opensearch_xml.format(method=method, host=hostname)