[mod] typification of SearXNG: add new result type File

This PR adds a new result type: File

    Python class: searx/result_types/file.py
    Jinja template: searx/templates/simple/result_templates/file.html
    CSS (less) client/simple/src/less/result_types/file.less

Class 'File' (singular) replaces template 'files.html' (plural).  The renaming
was carried out because there is only one file (singular) in a result. Not to be
confused with the category 'files' where in multiple results can exist.

As mentioned in issue [1], the class '.category-files' was removed from the CSS
and the stylesheet was adopted in result_types/file.less (there based on the
templates and no longer based on the category).

[1] https://github.com/searxng/searxng/issues/5198

Signed-off-by: Markus Heiser <markus.heiser@darmarit.de>
This commit is contained in:
Markus Heiser
2025-10-13 09:28:42 +02:00
committed by Markus Heiser
parent ee6d4f322f
commit 9371658531
14 changed files with 493 additions and 254 deletions
+3
View File
@@ -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
+14 -9
View File
@@ -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 = ""
+94
View File
@@ -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