diff options
Diffstat (limited to 'searx')
| -rw-r--r-- | searx/engines/recoll.py | 172 | ||||
| -rw-r--r-- | searx/engines/wikicommons.py | 258 | ||||
| -rw-r--r-- | searx/result_types/__init__.py | 3 | ||||
| -rw-r--r-- | searx/result_types/_base.py | 23 | ||||
| -rw-r--r-- | searx/result_types/file.py | 94 | ||||
| -rw-r--r-- | searx/settings.yml | 14 | ||||
| -rw-r--r-- | searx/templates/simple/result_templates/default.html | 2 | ||||
| -rw-r--r-- | searx/templates/simple/result_templates/file.html | 74 | ||||
| -rw-r--r-- | searx/templates/simple/result_templates/files.html | 45 |
9 files changed, 464 insertions, 221 deletions
diff --git a/searx/engines/recoll.py b/searx/engines/recoll.py index b7499b5a5..ee97f330d 100644 --- a/searx/engines/recoll.py +++ b/searx/engines/recoll.py @@ -13,23 +13,12 @@ Configuration You must configure the following settings: -``base_url``: - Location where recoll-webui can be reached. +- :py:obj:`base_url` +- :py:obj:`mount_prefix` +- :py:obj:`dl_prefix` +- :py:obj:`search_dir` -``mount_prefix``: - Location where the file hierarchy is mounted on your *local* filesystem. - -``dl_prefix``: - Location where the file hierarchy as indexed by recoll can be reached. - -``search_dir``: - Part of the indexed file hierarchy to be search, if empty the full domain is - searched. - -Example -======= - -Scenario: +Example scenario: #. Recoll indexes a local filesystem mounted in ``/export/documents/reference``, #. the Recoll search interface can be reached at https://recoll.example.org/ and @@ -37,107 +26,128 @@ Scenario: .. code:: yaml - base_url: https://recoll.example.org/ + base_url: https://recoll.example.org mount_prefix: /export/documents dl_prefix: https://download.example.org - search_dir: '' + search_dir: "" Implementations =============== """ +import typing as t from datetime import date, timedelta -from json import loads from urllib.parse import urlencode, quote -# about +from searx.result_types import EngineResults + +if t.TYPE_CHECKING: + from searx.extended_types import SXNG_Response + from searx.search.processors import OnlineParams + + about = { "website": None, - "wikidata_id": 'Q15735774', - "official_api_documentation": 'https://www.lesbonscomptes.com/recoll/', + "wikidata_id": "Q15735774", + "official_api_documentation": "https://www.lesbonscomptes.com/recoll/", "use_official_api": True, "require_api_key": False, - "results": 'JSON', + "results": "JSON", } -# engine dependent config paging = True time_range_support = True -# parameters from settings.yml -base_url = None -search_dir = '' -mount_prefix = None -dl_prefix = None - -# embedded -embedded_url = '<{ttype} controls height="166px" ' + 'src="{url}" type="{mtype}"></{ttype}>' +base_url: str = "" +"""Location where recoll-webui can be reached.""" +mount_prefix: str = "" +"""Location where the file hierarchy is mounted on your *local* filesystem.""" -# helper functions -def get_time_range(time_range): - sw = {'day': 1, 'week': 7, 'month': 30, 'year': 365} # pylint: disable=invalid-name - - offset = sw.get(time_range, 0) - if not offset: - return '' - - return (date.today() - timedelta(days=offset)).isoformat() +dl_prefix: str = "" +"""Location where the file hierarchy as indexed by recoll can be reached.""" +search_dir: str = "" +"""Part of the indexed file hierarchy to be search, if empty the full domain is +searched.""" -# do search-request -def request(query, params): - search_after = get_time_range(params['time_range']) - search_url = base_url + 'json?{query}&highlight=0' - params['url'] = search_url.format( - query=urlencode({'query': query, 'page': params['pageno'], 'after': search_after, 'dir': search_dir}) - ) +_s2i: dict[str | None, int] = {"day": 1, "week": 7, "month": 30, "year": 365} - return params +def setup(engine_settings: dict[str, t.Any]) -> bool: + """Initialization of the Recoll engine, checks if the mandatory values are + configured. + """ + missing: list[str] = [] + for cfg_name in ["base_url", "mount_prefix", "dl_prefix"]: + if not engine_settings.get(cfg_name): + missing.append(cfg_name) + if missing: + logger.error("missing recoll configuration: %s", missing) + return False -# get response from search-request -def response(resp): - results = [] + if engine_settings["base_url"].endswith("/"): + engine_settings["base_url"] = engine_settings["base_url"][:-1] + return True - response_json = loads(resp.text) - if not response_json: - return [] +def search_after(time_range: str | None) -> str: + offset = _s2i.get(time_range, 0) + if not offset: + return "" + return (date.today() - timedelta(days=offset)).isoformat() - for result in response_json.get('results', []): - title = result['label'] - url = result['url'].replace('file://' + mount_prefix, dl_prefix) - content = '{}'.format(result['snippet']) - # append result - item = {'url': url, 'title': title, 'content': content, 'template': 'files.html'} +def request(query: str, params: "OnlineParams") -> None: + args = { + "query": query, + "page": params["pageno"], + "after": search_after(params["time_range"]), + "dir": search_dir, + "highlight": 0, + } + params["url"] = f"{base_url}/json?{urlencode(args)}" - if result['size']: - item['size'] = int(result['size']) - for parameter in ['filename', 'abstract', 'author', 'mtype', 'time']: - if result[parameter]: - item[parameter] = result[parameter] +def response(resp: "SXNG_Response") -> EngineResults: - # facilitate preview support for known mime types - if 'mtype' in result and '/' in result['mtype']: - (mtype, subtype) = result['mtype'].split('/') - item['mtype'] = mtype - item['subtype'] = subtype + res = EngineResults() + json_data = resp.json() - if mtype in ['audio', 'video']: - item['embedded'] = embedded_url.format( - ttype=mtype, url=quote(url.encode('utf8'), '/:'), mtype=result['mtype'] - ) + if not json_data: + return res - if mtype in ['image'] and subtype in ['bmp', 'gif', 'jpeg', 'png']: - item['thumbnail'] = url + for result in json_data.get("results", []): - results.append(item) + url = result.get("url", "").replace("file://" + mount_prefix, dl_prefix) - if 'nres' in response_json: - results.append({'number_of_results': response_json['nres']}) + mtype = subtype = result.get("mime", "") + if mtype: + mtype, subtype = (mtype.split("/", 1) + [""])[:2] - return results + # facilitate preview support for known mime types + thumbnail = embedded = "" + if mtype in ["audio", "video"]: + embedded_url = '<{ttype} controls height="166px" ' + 'src="{url}" type="{mtype}"></{ttype}>' + embedded = embedded_url.format(ttype=mtype, url=quote(url.encode("utf8"), "/:"), mtype=result["mtype"]) + if mtype in ["image"] and subtype in ["bmp", "gif", "jpeg", "png"]: + thumbnail = url + + res.add( + res.types.File( + title=result.get("label", ""), + url=url, + content=result.get("snippet", ""), + size=result.get("size", ""), + filename=result.get("filename", ""), + abstract=result.get("abstract", ""), + author=result.get("author", ""), + mtype=mtype, + subtype=subtype, + time=result.get("time", ""), + embedded=embedded, + thumbnail=thumbnail, + ) + ) + return res diff --git a/searx/engines/wikicommons.py b/searx/engines/wikicommons.py index 718cd45d5..1cb2cb577 100644 --- a/searx/engines/wikicommons.py +++ b/searx/engines/wikicommons.py @@ -1,102 +1,208 @@ # SPDX-License-Identifier: AGPL-3.0-or-later -"""Wikimedia Commons (images)""" +"""`Wikimedia Commons`_ is a collection of more than 120 millions freely usable +media files to which anyone can contribute. -import datetime +This engine uses the `MediaWiki query API`_, with which engines can be configured +for searching images, videos, audio, and other files in the Wikimedia. + +.. _MediaWiki query API: https://commons.wikimedia.org/w/api.php?action=help&modules=query +.. _Wikimedia Commons: https://commons.wikimedia.org/ + + +Configuration +============= + +The engine has the following additional settings: + +.. code:: yaml + + - name: wikicommons.images + engine: wikicommons + wc_search_type: image + + - name: wikicommons.videos + engine: wikicommons + wc_search_type: video + + - name: wikicommons.audio + engine: wikicommons + wc_search_type: audio + + - name: wikicommons.files + engine: wikicommons + wc_search_type: file -from urllib.parse import urlencode + +Implementations +=============== + +""" + +import typing as t + +import datetime +import pathlib +from urllib.parse import urlencode, unquote from searx.utils import html_to_text, humanize_bytes +from searx.result_types import EngineResults + +if t.TYPE_CHECKING: + from searx.extended_types import SXNG_Response + from searx.search.processors import OnlineParams -# about about = { - "website": 'https://commons.wikimedia.org/', - "wikidata_id": 'Q565', - "official_api_documentation": 'https://commons.wikimedia.org/w/api.php', + "website": "https://commons.wikimedia.org/", + "wikidata_id": "Q565", + "official_api_documentation": "https://commons.wikimedia.org/w/api.php", "use_official_api": True, "require_api_key": False, - "results": 'JSON', + "results": "JSON", } -categories = ['images'] -search_type = 'images' - -base_url = "https://commons.wikimedia.org" -search_prefix = ( - '?action=query' - '&format=json' - '&generator=search' - '&gsrnamespace=6' - '&gsrprop=snippet' - '&prop=info|imageinfo' - '&iiprop=url|size|mime' - '&iiurlheight=180' # needed for the thumb url -) + +categories: list[str] = [] paging = True number_of_results = 10 -search_types = { - 'images': 'bitmap|drawing', - 'videos': 'video', - 'audio': 'audio', - 'files': 'multimedia|office|archive|3d', +wc_api_url = "https://commons.wikimedia.org/w/api.php" +wc_search_type: str = "" + +SEARCH_TYPES: dict[str, str] = { + "image": "bitmap|drawing", + "video": "video", + "audio": "audio", + "file": "multimedia|office|archive|3d", } +# FileType = t.Literal["bitmap", "drawing", "video", "audio", "multimedia", "office", "archive", "3d"] +# FILE_TYPES = list(t.get_args(FileType)) -def request(query, params): - language = 'en' - if params['language'] != 'all': - language = params['language'].split('-')[0] +def setup(engine_settings: dict[str, t.Any]) -> bool: + """Initialization of the Wikimedia engine, checks if the value configured in + :py:obj:`wc_search_type` is valid.""" - if search_type not in search_types: - raise ValueError(f"Unsupported search type: {search_type}") + if engine_settings.get("wc_search_type") not in SEARCH_TYPES: + logger.error( + "wc_search_type: %s isn't a valid file type (%s)", + engine_settings.get("wc_search_type"), + ",".join(SEARCH_TYPES.keys()), + ) + return False + return True - filetype = search_types[search_type] +def request(query: str, params: "OnlineParams") -> None: + uselang: str = "en" + if params["searxng_locale"] != "all": + uselang = params["searxng_locale"].split("-")[0] + filetype = SEARCH_TYPES[wc_search_type] args = { - 'uselang': language, - 'gsrlimit': number_of_results, - 'gsroffset': number_of_results * (params["pageno"] - 1), - 'gsrsearch': f"filetype:{filetype} {query}", + # https://commons.wikimedia.org/w/api.php + "format": "json", + "uselang": uselang, + "action": "query", + # https://commons.wikimedia.org/w/api.php?action=help&modules=query + "prop": "info|imageinfo", + # generator (gsr optins) https://commons.wikimedia.org/w/api.php?action=help&modules=query%2Bsearch + "generator": "search", + "gsrnamespace": "6", # https://www.mediawiki.org/wiki/Help:Namespaces#Renaming_namespaces + "gsrprop": "snippet", + "gsrlimit": number_of_results, + "gsroffset": number_of_results * (params["pageno"] - 1), + "gsrsearch": f"filetype:{filetype} {query}", + # imageinfo: https://commons.wikimedia.org/w/api.php?action=help&modules=query%2Bimageinfo + "iiprop": "url|size|mime", + "iiurlheight": "180", # needed for the thumb url } + params["url"] = f"{wc_api_url}?{urlencode(args, safe=':|')}" - params["url"] = f"{base_url}/w/api.php{search_prefix}&{urlencode(args, safe=':|')}" - return params +def response(resp: "SXNG_Response") -> EngineResults: -def response(resp): - results = [] - json = resp.json() + res = EngineResults() + json_data = resp.json() + pages = json_data.get("query", {}).get("pages", {}).values() - if not json.get("query", {}).get("pages"): - return results - for item in json["query"]["pages"].values(): + for item in pages: + + if not item.get("imageinfo", []): + continue imageinfo = item["imageinfo"][0] - title = item["title"].replace("File:", "").rsplit('.', 1)[0] - result = { - 'url': imageinfo["descriptionurl"], - 'title': title, - 'content': html_to_text(item["snippet"]), - } - - if search_type == "images": - result['template'] = 'images.html' - result['img_src'] = imageinfo["url"] - result['thumbnail_src'] = imageinfo["thumburl"] - result['resolution'] = f'{imageinfo["width"]} x {imageinfo["height"]}' - else: - result['thumbnail'] = imageinfo["thumburl"] - - if search_type == "videos": - result['template'] = 'videos.html' - if imageinfo.get('duration'): - result['length'] = datetime.timedelta(seconds=int(imageinfo['duration'])) - result['iframe_src'] = imageinfo['url'] - elif search_type == "files": - result['template'] = 'files.html' - result['metadata'] = imageinfo['mime'] - result['size'] = humanize_bytes(imageinfo['size']) - elif search_type == "audio": - result['iframe_src'] = imageinfo['url'] - - results.append(result) - - return results + + title: str = item["title"].replace("File:", "").rsplit(".", 1)[0] + content = html_to_text(item["snippet"]) + + url: str = imageinfo["descriptionurl"] + media_url: str = imageinfo["url"] + mimetype: str = imageinfo["mime"] + thumbnail: str = imageinfo["thumburl"] + size = imageinfo.get("size") + if size: + size = humanize_bytes(size) + + duration = None + seconds: str = imageinfo.get("duration") + if seconds: + try: + duration = datetime.timedelta(seconds=int(seconds)) + except OverflowError: + pass + + if wc_search_type == "file": + res.add( + res.types.File( + title=title, + url=url, + content=content, + size=size, + mimetype=mimetype, + filename=unquote(pathlib.Path(media_url).name), + embedded=media_url, + thumbnail=thumbnail, + ) + ) + continue + + if wc_search_type == "image": + res.add( + res.types.LegacyResult( + template="images.html", + title=title, + url=url, + content=content, + img_src=imageinfo["url"], + thumbnail_src=thumbnail, + resolution=f"{imageinfo['width']} x {imageinfo['height']}", + img_format=imageinfo["mime"], + filesize=size, + ) + ) + continue + + if wc_search_type == "video": + res.add( + res.types.LegacyResult( + template="videos.html", + title=title, + url=url, + content=content, + iframe_src=media_url, + length=duration, + ) + ) + continue + + if wc_search_type == "audio": + res.add( + res.types.MainResult( + template="default.html", + title=title, + url=url, + content=content, + audio_src=media_url, + length=duration, + ) + ) + continue + + return res diff --git a/searx/result_types/__init__.py b/searx/result_types/__init__.py index a1976c10f..4ae2de816 100644 --- a/searx/result_types/__init__.py +++ b/searx/result_types/__init__.py @@ -23,6 +23,7 @@ __all__ = [ "WeatherAnswer", "Code", "Paper", + "File", ] import typing as t @@ -33,6 +34,7 @@ from .answer import AnswerSet, Answer, Translations, WeatherAnswer from .keyvalue import KeyValue from .code import Code from .paper import Paper +from .file import File class ResultList(list[Result | LegacyResult], abc.ABC): @@ -47,6 +49,7 @@ class ResultList(list[Result | LegacyResult], abc.ABC): KeyValue = KeyValue Code = Code Paper = Paper + File = File MainResult = MainResult Result = Result Translations = Translations diff --git a/searx/result_types/_base.py b/searx/result_types/_base.py index e97894b75..ffcf7f143 100644 --- a/searx/result_types/_base.py +++ b/searx/result_types/_base.py @@ -27,7 +27,6 @@ import typing as t import re import urllib.parse import warnings -import time import datetime from collections.abc import Callable @@ -236,13 +235,6 @@ class Result(msgspec.Struct, kw_only=True): url: str | None = None """A link related to this *result*""" - template: str = "default.html" - """Name of the template used to render the result. - - By default :origin:`result_templates/default.html - <searx/templates/simple/result_templates/default.html>` is used. - """ - engine: str | None = "" """Name of the engine *this* result comes from. In case of *plugins* a prefix ``plugin:`` is set, in case of *answerer* prefix ``answerer:`` is @@ -350,6 +342,13 @@ class Result(msgspec.Struct, kw_only=True): class MainResult(Result): # pylint: disable=missing-class-docstring """Base class of all result types displayed in :ref:`area main results`.""" + template: str = "default.html" + """Name of the template used to render the result. + + By default :origin:`result_templates/default.html + <searx/templates/simple/result_templates/default.html>` is used. + """ + title: str = "" """Link title of the result item.""" @@ -359,6 +358,12 @@ class MainResult(Result): # pylint: disable=missing-class-docstring img_src: str = "" """URL of a image that is displayed in the result item.""" + iframe_src: str = "" + """URL of an embedded ``<iframe>`` / the frame is collapsible.""" + + audio_src: str = "" + """URL of an embedded ``<audio controls>``.""" + thumbnail: str = "" """URL of a thumbnail that is displayed in the result item.""" @@ -372,7 +377,7 @@ class MainResult(Result): # pylint: disable=missing-class-docstring completely eliminated. """ - length: time.struct_time | None = None + length: datetime.timedelta | None = None """Playing duration in seconds.""" views: str = "" diff --git a/searx/result_types/file.py b/searx/result_types/file.py new file mode 100644 index 000000000..5b58116c2 --- /dev/null +++ b/searx/result_types/file.py @@ -0,0 +1,94 @@ +# SPDX-License-Identifier: AGPL-3.0-or-later +""" +Typification of the *file* results. Results of this type are rendered in +the :origin:`file.html <searx/templates/simple/result_templates/file.html>` +template. + +---- + +.. autoclass:: File + :members: + :show-inheritance: + +""" +# pylint: disable=too-few-public-methods + + +__all__ = ["File"] + +import typing as t +import mimetypes + +from ._base import MainResult + + +@t.final +class File(MainResult, kw_only=True): + """Class for results of type *file*""" + + template: str = "file.html" + + filename: str = "" + """Name of the file.""" + + size: str = "" + """Size of bytes in human readable notation (``MB`` for 1024 * 1024 Bytes + file size.)""" + + time: str = "" + """Indication of a time, such as the date of the last modification or the + date of creation. This is a simple string, the *date* of which can be freely + chosen according to the context.""" + + mimetype: str = "" + """Mimetype/Subtype of the file. For ``audio`` and ``video``, a URL can be + passed in the :py:obj:`File.embedded` field to embed the referenced media in + the result. If no value is specified, the MIME type is determined from + ``self.filename`` or, alternatively, from ``self.embedded`` (if either of + the two values is set).""" + + abstract: str = "" + """Abstract of the file.""" + + author: str = "" + """Author of the file.""" + + embedded: str = "" + """URL of an embedded media type (audio or video) / is collapsible.""" + + mtype: str = "" + """Used for displaying :py:obj:`File.embedded`. Its value is automatically + populated from the base type of :py:obj:`File.mimetype`, and can be + explicitly set to enforce e.g. ``audio`` or ``video`` when mimetype is + something like "application/ogg" but its know the content is for example a + video.""" + + subtype: str = "" + """Used for displaying :py:obj:`File.embedded`. Its value is automatically + populated from the subtype type of :py:obj:`File.mimetype`, and can be + explicitly set to enforce a subtype for the :py:obj:`File.embedded` + element.""" + + def __post_init__(self): + super().__post_init__() + + if not self.mtype or not self.subtype: + + fn = self.filename or self.embedded + if not self.mimetype and fn: + self.mimetype = mimetypes.guess_type(fn, strict=False)[0] or "" + + mtype, subtype = (self.mimetype.split("/", 1) + [""])[:2] + + if not self.mtype: + # I don't know why, but the ogg video stream is not displayed, + # may https://github.com/videojs/video.js can help? + if self.embedded.endswith(".ogv"): + self.mtype = "video" + elif self.embedded.endswith(".oga"): + self.mtype = "audio" + else: + self.mtype = mtype + + if not self.subtype: + self.subtype = subtype diff --git a/searx/settings.yml b/searx/settings.yml index d742c517f..2e418177a 100644 --- a/searx/settings.yml +++ b/searx/settings.yml @@ -2298,31 +2298,27 @@ engines: - name: wikicommons.images engine: wikicommons - shortcut: wc + shortcut: wci categories: images - search_type: images - number_of_results: 10 + wc_search_type: image - name: wikicommons.videos engine: wikicommons shortcut: wcv categories: videos - search_type: videos - number_of_results: 10 + wc_search_type: video - name: wikicommons.audio engine: wikicommons shortcut: wca categories: music - search_type: audio - number_of_results: 10 + wc_search_type: audio - name: wikicommons.files engine: wikicommons shortcut: wcf categories: files - search_type: files - number_of_results: 10 + wc_search_type: file - name: wolframalpha shortcut: wa diff --git a/searx/templates/simple/result_templates/default.html b/searx/templates/simple/result_templates/default.html index 8a6329248..dcf6ad142 100644 --- a/searx/templates/simple/result_templates/default.html +++ b/searx/templates/simple/result_templates/default.html @@ -3,7 +3,7 @@ {{ result_header(result, favicons, image_proxify) -}} {{- result_sub_header(result) -}} {% if result.iframe_src -%} -<p class="altlink"><a class="btn-collapse collapsed media-loader disabled_if_nojs" data-target="#result-media-{{ index }}" data-btn-text-collapsed="{{ _('show media') }}" data-btn-text-not-collapsed="{{ _('hide media') }}">{{ icon('music-note') }} {{ _('show media') }}</a></p> +<p class="altlink"><a class="btn-collapse collapsed media-loader disabled_if_nojs" data-target="#result-media-{{ index }}" data-btn-text-collapsed="{{ _('show media') }}" data-btn-text-not-collapsed="{{ _('hide media') }}">{{ icon('play') }} {{ _('show media') }}</a></p> {%- endif %} {%- if result.content %} <p class="content"> diff --git a/searx/templates/simple/result_templates/file.html b/searx/templates/simple/result_templates/file.html new file mode 100644 index 000000000..c2ec66ede --- /dev/null +++ b/searx/templates/simple/result_templates/file.html @@ -0,0 +1,74 @@ +{% from "simple/macros.html" import result_header, result_sub_header, result_sub_footer, result_footer, result_link with context %} +{% from "simple/icons.html" import icon_small %} + +{{ result_header(result, favicons, image_proxify) }} +{{ result_sub_header(result) }} + +{% if result.abstract %} + <p class="abstract">{{ result.abstract|safe }}</p> +{% endif -%} + +{%- if result.content %} + <p class="content">{{ result.content|safe }}</p> +{% endif -%} + +<div class="attributes"> + {% if result.author %} + <div> + <span>{{ _("Author") }}:</span> + <span>{{ result.author }}</span> + </div> + {% endif %} + {% if result.filename %} + <div> + <span>{{ _("Filename") }}:</span> + <span>{{ result.filename }}</span> + </div> + {% endif %} + {% if result.size %} + <div> + <span>{{ _("Filesize") }}:</span> + <span>{{ result.size }}</span> + </div> + {% endif %} + {% if result.time %} + <div> + <span>{{ _("Date") }}:</span> + <span>{{ result.time }}</span> + </div> + {% endif %} + {% if result.mimetype %} + <div> + <span>{{ _("Type") }}:</span> + <span>{{ result.mimetype }}</span> + </div> + {% endif %} +</div> + +{% if result.embedded %} + {% if result.mtype in ("audio", "video") %} + <p class="altlink"> + <a class="btn-collapse collapsed media-loader disabled_if_nojs" + data-target="#result-media-{{ index }}" + data-btn-text-collapsed="{{ _("show media") }}" + data-btn-text-not-collapsed="{{ _("hide media") }}" + > + {{ _("show media") }} + </a> + </p> + <div id="result-media-{{ index }}" class="embedded-{{ result.mtype }} invisible"> + <{{ result.mtype }} controls preload="metadata" {% if result.thumbnail %}poster="{{ result.thumbnail }}" {% endif %}> + <source src="{{result.embedded}}" type="{{ result.mtype }}/{{ result.subtype }}"> + </{{ result.mtype }}> + </div> + {% else %} + <p class="altlink"> + <a href="{{result.embedded }}" target="_blank" rel="noopener noreferrer" download> + {{ _("Download") }} + </a> + </p> + {% endif %} +{% endif %} + +{{ result_sub_footer(result) }} +{{ result_footer(result) }} diff --git a/searx/templates/simple/result_templates/files.html b/searx/templates/simple/result_templates/files.html deleted file mode 100644 index 13a425f31..000000000 --- a/searx/templates/simple/result_templates/files.html +++ /dev/null @@ -1,45 +0,0 @@ -{% from 'simple/macros.html' import result_header, result_sub_header, result_sub_footer, result_footer, result_link with context %} -{% from 'simple/icons.html' import icon_small %} - -{{- result_header(result, favicons, image_proxify) -}} -{{- result_sub_header(result) -}} - -{%- if result.embedded -%} - <small> • <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') }}"> - {%- if result.mtype == 'audio' %}{{ icon_small('musical-notes') -}} - {%- elif result.mtype == 'video' %} {{ icon_small('play') -}} - {%- endif %} {{ _('show media') }}</a></small> -{%- endif -%} - -{%- if result.embedded -%} -<div id="result-media-{{ index }}" class="collapse invisible"> - {{- result.embedded|safe -}} -</div> -{%- endif -%} - -{%- if result.abstract %}<p class="result-content result-abstract">{{ result.abstract|safe }}</p>{% endif -%} - -{%- if result.img_src -%} -<div class="container-fluid"> - <div class="row"> -<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"> -{%- if result.content %}<p class="result-content col-xs-8 col-sm-8 col-md-8">{{ result.content|safe }}</p>{% endif -%} - </div> -</div> -{%- else -%} -{%- if result.content %}<p class="result-content">{{ result.content|safe }}</p>{% endif -%} -{%- endif -%} - -<table class="result-metadata result-content"> -{%- if result.author %}<tr><td>{{ _('Author') }}</td><td>{{ result.author|safe }}</td></tr>{% endif -%} - -{%- if result.filename %}<tr><td>{{ _('Filename') }}</td><td>{{ result.filename|safe }}</td></tr>{% endif -%} - -{%- if result.size %}<tr><td>{{ _('Filesize') }}</td><td>{{ result.size|safe }}</td></tr>{%- endif -%} - -{%- if result.time %}<tr><td>{{ _('Date') }}</td><td>{{ result.time|safe }}</td></tr>{% endif -%} - -{%- if result.mtype %}<tr><td>{{ _('Type') }}</td><td>{{ result.mtype|safe }}/{{ result.subtype|safe }}</td></tr>{% endif -%} -</table> - -{{ result_footer(result) }} |