# SPDX-License-Identifier: AGPL-3.0-or-later
""".. sidebar:: info
   - `Recoll <https://www.lesbonscomptes.com/recoll/>`_
   - `recoll-webui <https://framagit.org/medoc92/recollwebui.git>`_
   - :origin:`searx/engines/recoll.py`
Recoll_ is a desktop full-text search tool based on Xapian.  By itself Recoll_
does not offer WEB or API access, this can be achieved using recoll-webui_
Configuration
=============
You must configure the following settings:
- :py:obj:`base_url`
- :py:obj:`mount_prefix`
- :py:obj:`dl_prefix`
- :py:obj:`search_dir`
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
#. the contents of this filesystem can be reached though https://download.example.org/reference
.. code:: yaml
   base_url: https://recoll.example.org
   mount_prefix: /export/documents
   dl_prefix: https://download.example.org
   search_dir: ""
Implementations
===============
"""
import typing as t
from datetime import date, timedelta
from urllib.parse import urlencode, quote
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/",
    "use_official_api": True,
    "require_api_key": False,
    "results": "JSON",
}
paging = True
time_range_support = True
base_url: str = ""
"""Location where recoll-webui can be reached."""
mount_prefix: str = ""
"""Location where the file hierarchy is mounted on your *local* filesystem."""
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."""
_s2i: dict[str | None, int] = {"day": 1, "week": 7, "month": 30, "year": 365}
[docs]
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
    if engine_settings["base_url"].endswith("/"):
        engine_settings["base_url"] = engine_settings["base_url"][:-1]
    return True 
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()
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)}"
def response(resp: "SXNG_Response") -> EngineResults:
    res = EngineResults()
    json_data = resp.json()
    if not json_data:
        return res
    for result in json_data.get("results", []):
        url = result.get("url", "").replace("file://" + mount_prefix, dl_prefix)
        mtype = subtype = result.get("mime", "")
        if mtype:
            mtype, subtype = (mtype.split("/", 1) + [""])[:2]
        # 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