Просмотр исходного кода

Merge 78c9196bcb3b5de94cf800e9fd3501d356aa7bb5 into f82ead3e303d75ba63a370dc038311e172e1330d

Frank de Lange 6 лет назад
Родитель
Сommit
d8c7f14e6d
Аккаунт пользователя с таким Email не найден

+ 106
- 0
searx/engines/recoll.py Просмотреть файл

@@ -0,0 +1,106 @@
1
+"""
2
+ Recoll (local search engine)
3
+
4
+ @using-api   yes
5
+ @results     JSON
6
+ @stable      yes
7
+ @parse       url, content, size, abstract, author, mtype, subtype, time, \
8
+              filename, label, type, embedded
9
+"""
10
+
11
+from json import loads
12
+from searx.url_utils import urlencode, quote
13
+from datetime import date, timedelta
14
+
15
+# engine dependent config
16
+paging = True
17
+time_range_support = True
18
+
19
+# parameters from settings.yml
20
+base_url = None
21
+search_dir = ''
22
+mount_prefix = None
23
+dl_prefix = None
24
+
25
+# embedded
26
+embedded_url = '<{ttype} controls height="166px" ' +\
27
+    'src="{url}" type="{mtype}"></{ttype}>'
28
+
29
+
30
+# helper functions
31
+def get_time_range(time_range):
32
+    sw = {
33
+        'day': 1,
34
+        'week': 7,
35
+        'month': 30,
36
+        'year': 365
37
+    }
38
+
39
+    offset = sw.get(time_range, 0)
40
+    if not offset:
41
+        return ''
42
+
43
+    return (date.today() - timedelta(days=offset)).isoformat()
44
+
45
+
46
+# do search-request
47
+def request(query, params):
48
+    search_after = get_time_range(params['time_range'])
49
+    search_url = base_url + 'json?{query}&highlight=0'
50
+    params['url'] = search_url.format(query=urlencode({
51
+        'query': query,
52
+        'page': params['pageno'],
53
+        'after': search_after,
54
+        'dir': search_dir}))
55
+
56
+    return params
57
+
58
+
59
+# get response from search-request
60
+def response(resp):
61
+    results = []
62
+
63
+    response_json = loads(resp.text)
64
+
65
+    if not response_json:
66
+        return []
67
+
68
+    for result in response_json.get('results', []):
69
+        title = result['label']
70
+        url = result['url'].replace('file://' + mount_prefix, dl_prefix)
71
+        content = u'{}'.format(result['snippet'])
72
+
73
+        # append result
74
+        item = {'url': url,
75
+                'title': title,
76
+                'content': content,
77
+                'template': 'files.html'}
78
+
79
+        if result['size']:
80
+            item['size'] = int(result['size'])
81
+
82
+        for parameter in ['filename', 'abstract', 'author', 'mtype', 'time']:
83
+            if result[parameter]:
84
+                item[parameter] = result[parameter]
85
+
86
+        # facilitate preview support for known mime types
87
+        if 'mtype' in result:
88
+            (mtype, subtype) = result['mtype'].split('/')
89
+            item['mtype'] = mtype
90
+            item['subtype'] = subtype
91
+
92
+            if mtype in ['audio', 'video']:
93
+                item['embedded'] = embedded_url.format(
94
+                    ttype=mtype,
95
+                    url=quote(url.encode('utf8'), '/:'),
96
+                    mtype=result['mtype'])
97
+
98
+            if mtype in ['image'] and subtype in ['bmp', 'gif', 'jpeg', 'png']:
99
+                item['img_src'] = url
100
+
101
+        results.append(item)
102
+
103
+    if 'nres' in response_json:
104
+        results.append({'number_of_results': response_json['nres']})
105
+
106
+    return results

+ 58
- 0
searx/settings.yml Просмотреть файл

@@ -524,6 +524,64 @@ engines:
524 524
     shortcut : qws
525 525
     categories : social media
526 526
 
527
+    # recoll is a local search engine based on Xapian:
528
+    # http://www.lesbonscomptes.com/recoll/
529
+    #
530
+    # By itself recoll does not offer web or API access,
531
+    # this can be achieved using recoll-webui:
532
+    # https://github.com/koniu/recoll-webui
533
+    #
534
+    # As recoll-webui by default does not support paged JSON
535
+    # results it is advisable to use a patched version which does:
536
+    # https://github.com/Yetangitu/recoll-webui/tree/jsonpage
537
+    # A pull request was sent upstream, if this is merged the patched
538
+    # version is no longer needed
539
+    #
540
+    # This engine uses a custom 'files' result template
541
+    #
542
+    # set base_url to the location where recoll-webui can be reached
543
+    # set mount_prefix to the location where the file hierarchy is mounted on your _local_ filesystem
544
+    # set dl_prefix to a location where the file hierarchy as indexed by recoll can be reached
545
+    # 
546
+    # For example:
547
+    #
548
+    #     Recoll indexes a local filesystem mounted in /export/documents/reference
549
+    #     The Recoll search inteface can be reached at https://recoll.example.org/
550
+    #     The contents of this filesystem can be reached though https://download.example.org/reference
551
+    #
552
+    #        set base_url to https://recoll.example.org/
553
+    #        set mount_prefix to /export/documents
554
+    #        set dl_prefix to https://download.example.org
555
+    #
556
+    #     the resulting url will be https://download.example.org/reference
557
+    # 
558
+    # set search_dir to the part of the indexed file hierarchy to be searched, use an empty string
559
+    #     to search the entire search domain
560
+
561
+    # this entry (with search_dir set to an empty string) covers the entire recoll search domain
562
+#  - name: library
563
+#    engine: recoll
564
+#    shortcut: lib
565
+#    base_url: 'https://recoll.example.org/'
566
+#    search_dir: ''
567
+#    mount_prefix: /export
568
+#    dl_prefix: 'https://download.example.org'
569
+#    timeout: 30.0
570
+#    categories: files
571
+#    disabled: True
572
+
573
+    # this entry only searches the 'reference' directory
574
+#  - name: library reference
575
+#    engine: recoll
576
+#    base_url: 'https://recoll.example.org/'
577
+#    search_dir: reference
578
+#    mount_prefix: /export
579
+#    dl_prefix: 'https://download.example.org'
580
+#    shortcut: libr
581
+#    timeout: 30.0
582
+#    categories: files
583
+#    disabled: True
584
+
527 585
   - name : reddit
528 586
     engine : reddit
529 587
     shortcut : re

+ 19
- 0
searx/static/themes/oscar/less/logicodev/results.less Просмотреть файл

@@ -41,6 +41,11 @@
41 41
 
42 42
 }
43 43
 
44
+.result-abstract {
45
+    margin-top: 0.5em;
46
+    margin-bottom: 0.8em;
47
+}
48
+
44 49
 .external-link {
45 50
     color: @dark-green;
46 51
     font-size: 12px;
@@ -114,6 +119,20 @@
114 119
     }
115 120
 }
116 121
 
122
+.result-metadata {
123
+    clear: both;
124
+    margin: 1em;
125
+
126
+    td {
127
+        padding-right: 1em;
128
+        color: @gray;
129
+    }
130
+
131
+    td:first-of-type {
132
+        color: @dark-gray;
133
+    }
134
+}
135
+
117 136
 // map formating of results
118 137
 .result-map {
119 138
     clear: both;

+ 26
- 0
searx/templates/oscar/macros.html Просмотреть файл

@@ -39,6 +39,20 @@
39 39
 <div class="external-link">{{ result.pretty_url }}</div>
40 40
 {%- endmacro %}
41 41
 
42
+<!-- Draw result footer without cache link -->
43
+{% macro result_footer_nocache(result) -%}
44
+    <div class="clearfix"></div>
45
+    <div class="pull-right">
46
+    {% for engine in result.engines %}
47
+        <span class="label label-default">{{ engine }}</span>
48
+    {% endfor %}
49
+    {% if proxify %}
50
+    <small>{{ result_link(proxify(result.url), icon('sort') + _('proxied'), "text-info") }}</small>
51
+    {% endif %}
52
+</div>
53
+<div class="external-link">{{ result.pretty_url }}</div>
54
+{%- endmacro %}
55
+
42 56
 <!-- Draw result footer -->
43 57
 {% macro result_footer_rtl(result) -%}
44 58
     <div class="clearfix"></div>
@@ -52,6 +66,18 @@
52 66
     <div class="external-link">{{ result.pretty_url }}</div>
53 67
 {%- endmacro %}
54 68
 
69
+<!-- Draw result footer without cache link -->
70
+{% macro result_footer_nocache_rtl(result) -%}
71
+    <div class="clearfix"></div>
72
+    {% for engine in result.engines %}
73
+        <span class="label label-default">{{ engine }}</span>
74
+    {% endfor %}
75
+    {% if proxify %}
76
+    <small>{{ result_link(proxify(result.url), icon('sort') + _('proxied'), "text-info") }}</small>
77
+    {% endif %}
78
+    <div class="external-link">{{ result.pretty_url }}</div>
79
+{%- endmacro %}
80
+
55 81
 {% macro preferences_item_header(info, label, rtl) -%}
56 82
     {% if rtl %}
57 83
     <div class="row form-group">

+ 55
- 0
searx/templates/oscar/result_templates/files.html Просмотреть файл

@@ -0,0 +1,55 @@
1
+{% from 'oscar/macros.html' import result_header, result_sub_header, result_footer_nocache, result_footer_nocache_rtl, icon with context %}
2
+
3
+{{ result_header(result, favicons) }}
4
+{{ result_sub_header(result) }}
5
+
6
+{% if result.embedded %}
7
+    <small> &bull; <a class="text-info btn-collapse collapsed cursor-pointer media-loader disabled_if_nojs" data-toggle="collapse" data-target="#result-media-{{ index }}" data-btn-text-collapsed="{{ _('show media') }}" data-btn-text-not-collapsed="{{ _('hide media') }}">
8
+    {% if result.mtype == 'audio' %}{{ icon('music') }}
9
+    {% elif result.mtype == 'video' %} {{ icon('film') }}
10
+    {% endif %} {{ _('show media') }}</a></small>
11
+{% endif %}
12
+
13
+{% if result.embedded %}
14
+<div id="result-media-{{ index }}" class="collapse">
15
+   {{ result.embedded|safe }}
16
+</div>
17
+{% endif %}
18
+
19
+{% if result.abstract %}<p class="result-content result-abstract">{{ result.abstract|safe }}</p>{% endif %}
20
+
21
+{% if result.img_src %}
22
+<div class="container-fluid">
23
+    <div class="row">
24
+<img src="{{ image_proxify(result.img_src) }}" alt="{{ result.title|striptags }}" title="{{ result.title|striptags }}" style="width: auto; max-height: 60px; min-height: 60px;" class="col-xs-2 col-sm-4 col-md-4 result-content">
25
+{% if result.content %}<p class="result-content col-xs-8 col-sm-8 col-md-8">{{ result.content|safe }}</p>{% endif %}
26
+    </div>
27
+</div>
28
+{% else %}
29
+{% if result.content %}<p class="result-content">{{ result.content|safe }}</p>{% endif %}
30
+{% endif %}
31
+
32
+<table class="result-metadata result-content">
33
+{% if result.author %}<tr><td>{{ _('Author') }}</td><td>{{ result.author|safe }}</td></tr>{% endif %}
34
+
35
+{% if result.filename %}<tr><td>{{ _('Filename') }}</td><td>{{ result.filename|safe }}</td></tr>{% endif %}
36
+
37
+{% if result.size %}<tr><td>{{ _('Filesize') }}</td><td>
38
+        {% if result.size < 1024 %}{{ result.size }} {{ _('Bytes') }}
39
+        {% elif result.size < 1024*1024 %}{{ '{0:0.2f}'.format(result.size/1024) }} {{ _('kiB') }}
40
+        {% elif result.size < 1024*1024*1024 %}{{ '{0:0.2f}'.format(result.size/1024/1024) }} {{ _('MiB') }}
41
+        {% elif result.size < 1024*1024*1024*1024 %}{{ '{0:0.2f}'.format(result.size/1024/1024/1024) }} {{ _('GiB') }}
42
+        {% else %}{{ '{0:0.2f}'.format(result.size/1024/1024/1024/1024) }} {{ _('TiB') }}{% endif %}
43
+    </td></tr>
44
+{% endif %}
45
+
46
+{% if result.time %}<tr><td>{{ _('Date') }}</td><td>{{ result.time|safe }}</td></tr>{% endif %}
47
+
48
+{% if result.mtype %}<tr><td>{{ _('Type') }}</td><td>{{ result.mtype|safe }}/{{ result.subtype|safe }}</td></tr>{% endif %}
49
+</table>
50
+
51
+{% if rtl %}
52
+{{ result_footer_nocache_rtl(result) }}
53
+{% else %}
54
+{{ result_footer_nocache(result) }}
55
+{% endif %}