123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300 |
- ########################################################################
- # Searx-Qt - Lightweight desktop application for Searx.
- # Copyright (C) 2020-2022 CYBERDEViL
- #
- # This file is part of Searx-Qt.
- #
- # Searx-Qt is free software: you can redistribute it and/or modify
- # it under the terms of the GNU General Public License as published by
- # the Free Software Foundation, either version 3 of the License, or
- # (at your option) any later version.
- #
- # Searx-Qt is distributed in the hope that it will be useful,
- # but WITHOUT ANY WARRANTY; without even the implied warranty of
- # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- # GNU General Public License for more details.
- #
- # You should have received a copy of the GNU General Public License
- # along with this program. If not, see <https://www.gnu.org/licenses/>.
- #
- ########################################################################
- import html
- from urllib.parse import quote
- from searxqt.utils.string import formatFileSize, formatFileCount
- from searxqt.translations import _
- class HtmlGen:
- def wrapHtml(bodyStr, styleStr=""):
- return f"""
- <html>
- <head>
- <style>{styleStr}</style>
- </head>
- <body>
- {bodyStr}
- </body>
- </html>"""
- class FailedResponsesHtml:
- def create(results, message, css):
- """
- @param result: List with failed search result object (searxqt.core.requests.Result).
- @type result: list
- @param message: TODO
-
- @param css: Optionaly set a stylesheet, it should be the content only;
- so do not include the <style> tags!
- @type css: str
- """
- body = f"<p id=\"top-message\">{message}</p>" \
- f"<h3>{_('Failed requests')}:</h3>" \
- "<table class=\"fail\">"
- lastResult = results[-1]
- for result in results:
- url = result.url()
- error = html.escape(result.error())
- content = html.escape(result.text())
- body += "<tr>" \
- "<td colspan=\"2\" class=\"fail-instance\">" \
- f"<a href=\"{url}\">{url}</a>" \
- "</td></tr>" \
- "<tr>" \
- f"<td class=\"fail-prefix\">{_('Error')}</td>" \
- f"<td class=\"fail-value\">{error}</td>" \
- "</tr>" \
- "<tr>" \
- f"<td class=\"fail-prefix\">{_('Content')}</td>" \
- f"<td class=\"fail-value\">{content}</td>" \
- "</tr>"
- if result != lastResult:
- body += "<tr><td colspan=\"2\"><hr></td></tr>"
- body += "</table>"
- return HtmlGen.wrapHtml(body, css)
- class ResultsHtml:
- """ Create HTML from a valid search response.
- """
- def create(jsonResult, css):
- """
- @param jsonResult: The josn result of a search response.
- @type jsonResult: json or dict
- @param css: Optionaly set a stylesheet, it should be the content only;
- so do not include the <style> tags!
- @type css: str
- """
- elemStr = ""
- # Search query suggestions
- data = jsonResult.get('suggestions', [])
- if data:
- elemStr += ResultsHtml.createHrefListElement(
- 'search', _('Suggestions'), data
- )
- elemStr += "<hr>"
- # Search query corrections
- data = jsonResult.get('corrections', [])
- if data:
- elemStr += ResultsHtml.createHrefListElement(
- 'search', _('Corrections'), data
- )
- elemStr += "<hr>"
- # Unresponsive engines
- data = jsonResult.get('unresponsive_engines', [])
- if data:
- elemStr += ResultsHtml.createUnresponsiveEnginesElement(data)
- elemStr += "<hr>"
- # Infoboxes
- data = jsonResult.get('infoboxes', [])
- if data:
- elemStr += "<p id=\"infoboxes_header\">Infoboxes</p>"
- for infoboxData in data:
- elemStr += ResultsHtml.createInfobox(infoboxData)
- # Answers
- data = jsonResult.get('answers', [])
- if data:
- elemStr += ResultsHtml.createStrListElement(_('Answers'), data)
- elemStr += "<hr>"
- # Results
- data = jsonResult.get('results', [])
- if data:
- for resultData in data:
- elemStr += ResultsHtml.createResultElement(resultData)
- return HtmlGen.wrapHtml(elemStr, css)
- def createStrListElement(title, data):
- elemStr = " | ".join([html.escape(entry) for entry in data])
- return ResultsHtml.createListContainer(title, elemStr)
- def createHrefListElement(scheme, title, data):
- elemStr = ""
- scheme += ":"
- for entry in data:
- if elemStr:
- elemStr += " | "
- url = scheme + quote(entry)
- elemStr += f"<a href=\"{url}\">{html.escape(entry)}</a>"
- return ResultsHtml.createListContainer(title, elemStr)
- def createListContainer(title, elems):
- return f"<div class=\"{title}\"><b>{title}</b>: {elems}</div>"
- def createUnresponsiveEnginesElement(data):
- elemStr = ""
- for name, error in data:
- if elemStr:
- elemStr += " | "
- elemStr += f"{html.escape(name)} <i>({html.escape(error)})</i>"
- return ResultsHtml.createListContainer(_('Unresponsive engines'), elemStr)
- def createInfobox(infoboxData):
- # Create new infobox
- elemStr = "<div class=\"infobox\">"
- # Title
- data = html.escape(infoboxData.get("infobox", "-"))
- elemStr += "<center><div class=\"infobox_title\">" \
- f"{data}</div></center>"
- # ID
- data = infoboxData.get("id", None)
- if data:
- data = html.escape(data)
- elemStr += "<center><div class=\"infobox_id\">" \
- f"<a href=\"{data}\">{data}</a></div></center>"
- # Content
- data = infoboxData.get("content", None)
- if data:
- data = html.escape(data)
- elemStr += f"<div class=\"infobox_content\">{data}</div>"
- # Attributes
- data = infoboxData.get("attributes", None)
- if data:
- elemStr += "<p class=\"infobox_attr_head\">Attributes</p>"
- elemStr += "<table class=\"infobox_attr_table\">"
- for attributeData in data:
- # TODO for now skip images ..
- if "value" not in attributeData:
- continue
- # New row
- elemStr += "<tr>"
- value = html.escape(attributeData.get('label', ''))
- elemStr += f"<td class=\"infobox_label\">{value}</td>"
- value = html.escape(attributeData.get('value', ''))
- elemStr += f"<td class=\"infobox_value\">{value}</td>"
- elemStr += "</tr>"
- elemStr += "</table>"
- # Urls list
- data = infoboxData.get("urls", None)
- if data:
- elemStr += "<p class=\"infobox_links_head\">Links</p>"
- elemStr += "<ul class=\"infobox_link_list\">"
- for urlData in data:
- url = html.escape(urlData.get("url"))
- title = html.escape(urlData.get("title", "-"))
- elemStr += "<li class=\"infobox_link_list_item\">" \
- f"<a href=\"{url}\" title=\"{url}\">{title}</a>" \
- "</li>"
- elemStr += "</ul>"
- # Engines
- data = infoboxData.get("engines", None)
- if data:
- elemStr += ResultsHtml.createStrListElement(_('Engines'), data)
- # Closing tag of the new infobox element.
- elemStr += "</div>"
- elemStr += "<hr>"
- return elemStr
- def createResultElement(data):
- # Create general elements
- title=html.escape(data.get('title', ''))
- url=html.escape(data.get('url', ''))
- content=html.escape(data.get('content', ''))
- engine = ''
- for e in data.get('engines', []):
- engine += f"{e} "
- engine = engine.rstrip()
- elemStr = "<div class=\"results\">" \
- f"<h4 class=\"result-title\"><i>{engine}: </i>" \
- f"<a href=\"{url}\">{title}</a></h4>" \
- "<div style=\"margin-left: 10px;\">" \
- f"<p class=\"result-description\">{content}</p>" \
- f"<p class=\"result-url\">{url}</p>"
- # Add file data elements
- elemStr += ResultsHtml.createFileSection(data)
- elemStr += "</div></div>"
- return elemStr
- def createFileSection(data):
- elemStr = ""
- value = data.get('magnetlink', '')
- if value:
- value = html.escape(value)
- elemStr += f"<a href=\"{value}\">Magnet</a> "
- value = data.get('torrentfile', '')
- if value:
- value = html.escape(value)
- elemStr += f"<a href=\"{value}\">Torrent</a> "
- value = data.get('filesize', 0)
- if value:
- elemStr += formatFileSize(value) + " "
- value = data.get('files', 0)
- if value:
- elemStr += formatFileCount(value) + " "
- value = data.get('seed', None)
- if value is not None:
- value = html.escape(str(value))
- elemStr += f"seeders: {value} "
- value = data.get('leech', None)
- if value is not None:
- value = html.escape(str(value))
- elemStr += f"leechers: {value} "
- value = data.get('img_format', "")
- if value:
- value = html.escape(value)
- elemStr += f"format: {value} "
- if elemStr:
- elemStr = "<p class=\"result-file\">" + elemStr + "</p>"
- return elemStr
|