diff --git a/searx/engines/360search_videos.py b/searx/engines/360search_videos.py index d359d534f..b661e9c5b 100644 --- a/searx/engines/360search_videos.py +++ b/searx/engines/360search_videos.py @@ -52,7 +52,7 @@ def response(resp) -> EngineResults: published_date = None res.add( - res.types.LegacyResult( + res.types.Video( url=entry["play_url"], title=html_to_text(entry["title"]), content=html_to_text(entry["description"]), diff --git a/searx/engines/brave.py b/searx/engines/brave.py index 459f0c8c9..98d34ce0b 100644 --- a/searx/engines/brave.py +++ b/searx/engines/brave.py @@ -127,7 +127,7 @@ from dateutil import parser from searx import locales, logger from searx.enginelib.traits import EngineTraits from searx.exceptions import SearxEngineResponseException -from searx.result_types import EngineResults, MainResult +from searx.result_types import EngineResults, MainResult, Video from searx.result_types.image import Image from searx.utils import html_to_text, js_obj_str_to_json_str, js_obj_str_to_python @@ -331,8 +331,7 @@ def _parse_video_result(result: dict[str, t.Any]) -> MainResult: video: dict[str, t.Any] = result.get("video", {}) thumbnail: dict[str, t.Any] = result.get("thumbnail", {}) - return MainResult( - template="videos.html", + return Video( title=result.get("title", ""), url=result.get("url"), content=result.get("description", ""), diff --git a/searx/engines/dogpile.py b/searx/engines/dogpile.py index 37a0354d5..2469dbc65 100644 --- a/searx/engines/dogpile.py +++ b/searx/engines/dogpile.py @@ -5,13 +5,13 @@ """ import typing as t -from datetime import datetime, timezone +from datetime import datetime, timezone, timedelta import html from searx.enginelib import EngineCache from searx.exceptions import SearxEngineAPIException from searx.network import post -from searx.utils import format_duration, html_to_text, humanize_number +from searx.utils import html_to_text, humanize_number from searx.result_types import EngineResults if t.TYPE_CHECKING: @@ -98,14 +98,13 @@ def response(resp: "SXNG_Response"): ) elif dogpile_categ == "videos": res.add( - res.types.LegacyResult( - template="videos.html", + res.types.Video( url=result["clickUrl"], title=html_to_text(result["title"]), content=html_to_text(result["description"]), thumbnail=result["thumbnailUrl"], publishedDate=datetime.fromisoformat(result["publishDate"]), - length=format_duration(result["duration"]), + length=timedelta(seconds=result["duration"]), views=humanize_number(result["viewCount"]), ) ) diff --git a/searx/engines/duckduckgo_extra.py b/searx/engines/duckduckgo_extra.py index 1c1fb1f30..58d6ba2ac 100644 --- a/searx/engines/duckduckgo_extra.py +++ b/searx/engines/duckduckgo_extra.py @@ -10,7 +10,7 @@ from datetime import datetime from urllib.parse import urlencode from urllib.parse import quote_plus -from searx.result_types import EngineResults, MainResult, LegacyResult, Image +from searx.result_types import EngineResults, MainResult, Image, Video from searx.utils import html_to_text, gen_useragent, extr from searx.network import get # see https://github.com/searxng/searxng/issues/762 @@ -162,13 +162,11 @@ def _image_result(result): def _video_result(result): - return LegacyResult( - template='videos.html', + return Video( url=result['content'], title=result['title'], content=result['description'], thumbnail=result['images'].get('small') or result['images'].get('medium'), - source=result['provider'], length=result['duration'], metadata=result.get('uploader'), ) diff --git a/searx/engines/findfiles.py b/searx/engines/findfiles.py index 397724aa0..68312247d 100644 --- a/searx/engines/findfiles.py +++ b/searx/engines/findfiles.py @@ -92,8 +92,7 @@ def response(resp: "SXNG_Response") -> EngineResults: ): video_src = extract_text(eval_xpath(result, ".//video/@src")) or "" res.add( - res.types.LegacyResult( - template="videos.html", + res.types.Video( url=video_src, title=extract_text(eval_xpath(result, ".//div[contains(@class, 'caption')]/span")) or "", iframe_src=video_src or "", diff --git a/searx/engines/fireball.py b/searx/engines/fireball.py index e7370038b..4c7de5322 100644 --- a/searx/engines/fireball.py +++ b/searx/engines/fireball.py @@ -153,16 +153,13 @@ def response(resp: "SXNG_Response") -> EngineResults: length = result["video"].get("duration") res.add( - res.types.LegacyResult( - { - "template": "videos.html", - "url": result["url"], - "title": html_to_text(result["title"]), - "content": html_to_text(result["description"]), - "thumbnail": result.get("thumbnail", {}).get("original"), - "length": length, - "publishedDate": published_date, - } + res.types.Video( + url=result["url"], + title=html_to_text(result["title"]), + content=html_to_text(result["description"]), + thumbnail=result.get("thumbnail", {}).get("original"), + length=length, + publishedDate=published_date, ) ) diff --git a/searx/engines/google_videos.py b/searx/engines/google_videos.py index a0a72fc12..c36833f6b 100644 --- a/searx/engines/google_videos.py +++ b/searx/engines/google_videos.py @@ -73,12 +73,11 @@ def response(resp: "SXNG_Response") -> EngineResults: break results.add( - results.types.MainResult( + results.types.Video( url=url, title=title, thumbnail=thumbnail, length=length, - template="videos.html", ) ) diff --git a/searx/engines/kagi.py b/searx/engines/kagi.py index 205e2c44a..1bcd0da59 100644 --- a/searx/engines/kagi.py +++ b/searx/engines/kagi.py @@ -178,17 +178,14 @@ def response(resp: "SXNG_Response") -> EngineResults: length = parse_duration_string(result["props"]["duration"]) res.add( - res.types.LegacyResult( - { - "template": "videos.html", - "url": result["url"], - "title": html_to_text(result.get("title", "no title available")), - "content": html_to_text(result.get("snippet", "")), - "thumbnail": result.get("image", {}).get("url"), - "publishedDate": published_date, - "author": result.get("props", {}).get("creator_name"), - "length": length, - } + res.types.Video( + url=result["url"], + title=html_to_text(result.get("title", "no title available")), + content=html_to_text(result.get("snippet", "")), + thumbnail=result.get("image", {}).get("url"), + publishedDate=published_date, + author=result.get("props", {}).get("creator_name"), + length=length, ) ) diff --git a/searx/engines/luxxle.py b/searx/engines/luxxle.py index a496b7a2a..3bb91a104 100644 --- a/searx/engines/luxxle.py +++ b/searx/engines/luxxle.py @@ -157,8 +157,7 @@ def _news_results(doc: ElementType, res: EngineResults): def _video_results(doc: ElementType, res: EngineResults): for result in eval_xpath_list(doc, "//div[@id='mainResults']/div[contains(@class, 'mediaResult')]"): res.add( - res.types.MainResult( - template="videos.html", + res.types.Video( url=extract_text(eval_xpath(result, "./@data-url")) or "", title=extract_text(eval_xpath(result, ".//div[contains(@class, 'mediaResultTitleVideo')]/a")) or "", content=extract_text(eval_xpath(result, ".//div[contains(@class, 'mediaResultDescription')]")) or "", diff --git a/searx/engines/mediathekviewweb.py b/searx/engines/mediathekviewweb.py index 93373b321..7bcb32e24 100644 --- a/searx/engines/mediathekviewweb.py +++ b/searx/engines/mediathekviewweb.py @@ -5,6 +5,7 @@ import typing as t import datetime from searx.result_types import EngineResults +from searx.utils import parse_duration_string if t.TYPE_CHECKING: from searx.extended_types import SXNG_Response @@ -67,13 +68,12 @@ def response(resp: "SXNG_Response") -> EngineResults: continue res.add( - res.types.LegacyResult( + res.types.Video( url=video_url, title="%(channel)s: %(title)s (%(hms)s)" % item, - length=item["hms"], + length=parse_duration_string(item["hms"]), content="%(description)s" % item, iframe_src=video_url, - template="videos.html", ) ) diff --git a/searx/engines/naver.py b/searx/engines/naver.py index 3cdd75027..63aba9fe8 100644 --- a/searx/engines/naver.py +++ b/searx/engines/naver.py @@ -201,7 +201,7 @@ def parse_videos(data): for item in eval_xpath_list(dom, "//li[contains(@class, 'video_item')]"): url = eval_xpath_getindex(item, ".//a[contains(@class, 'info_title')]/@href", 0) - thumbnail = None + thumbnail = "" try: thumbnail = eval_xpath_getindex(item, ".//img[contains(@class, 'thumb')]/@src", 0) except (ValueError, TypeError, SearxEngineXPathException): @@ -214,9 +214,8 @@ def parse_videos(data): pass res.add( - res.types.LegacyResult( - template="videos.html", - title=extract_text(eval_xpath(item, ".//a[contains(@class, 'info_title')]")), + res.types.Video( + title=extract_text(eval_xpath(item, ".//a[contains(@class, 'info_title')]")) or "", url=url, thumbnail=thumbnail, length=length, diff --git a/searx/engines/odysee.py b/searx/engines/odysee.py index e7d222370..e8314bbee 100644 --- a/searx/engines/odysee.py +++ b/searx/engines/odysee.py @@ -4,14 +4,19 @@ .. _Odysee: https://github.com/OdyseeTeam/odysee-frontend """ -from datetime import datetime +import typing as t +from datetime import datetime, timedelta from urllib.parse import urlencode import babel from searx.enginelib.traits import EngineTraits from searx.locales import language_tag -from searx.utils import format_duration +from searx.result_types import EngineResults + +if t.TYPE_CHECKING: + from searx.extended_types import SXNG_Response + from searx.search.processors import OnlineParams # Engine metadata about = { @@ -34,7 +39,7 @@ categories = ["videos"] base_url = "https://lighthouse.odysee.tv/search" -def request(query, params): +def request(query: str, params: "OnlineParams"): time_range_dict = { "day": "today", "week": "thisweek", @@ -59,22 +64,17 @@ def request(query, params): query_params["time_filter"] = time_range_dict[params["time_range"]] params["url"] = f"{base_url}?{urlencode(query_params)}" - return params -def response(resp): +def response(resp: "SXNG_Response") -> EngineResults: + res = EngineResults() data = resp.json() - results = [] for item in data: name = item["name"] claim_id = item["claimId"] - title = item["title"] thumbnail_url = item["thumbnail_url"] - description = item["description"] or "" - channel = item["channel"] release_time = item["release_time"] - duration = item["duration"] release_date = datetime.fromisoformat(release_time.split("T")[0]) formatted_date = datetime.fromtimestamp(release_date.timestamp()) @@ -82,23 +82,21 @@ def response(resp): url = f"https://odysee.com/{name}:{claim_id}" iframe_url = f"https://odysee.com/$/embed/{name}:{claim_id}" odysee_thumbnail = f"https://thumbnails.odycdn.com/optimize/s:390:0/quality:85/plain/{thumbnail_url}" - formatted_duration = format_duration(duration) - results.append( - { - "title": title, - "url": url, - "content": description, - "author": channel, - "publishedDate": formatted_date, - "length": formatted_duration, - "thumbnail": odysee_thumbnail, - "iframe_src": iframe_url, - "template": "videos.html", - } + res.add( + res.types.Video( + title=item["title"], + url=url, + content=item["description"] or "", + author=item["channel"], + publishedDate=formatted_date, + length=timedelta(seconds=item["duration"]), + thumbnail=odysee_thumbnail, + iframe_src=iframe_url, + ) ) - return results + return res def fetch_traits(engine_traits: EngineTraits): diff --git a/searx/engines/privacywall.py b/searx/engines/privacywall.py index 51995cfd3..085dad1a3 100644 --- a/searx/engines/privacywall.py +++ b/searx/engines/privacywall.py @@ -146,8 +146,7 @@ def _video_results(doc: "ElementBase") -> EngineResults: thumbnail = _extract_thumbnail_url(extr(thumbnail_style, ":url(", ")")) res.add( - res.types.LegacyResult( - template="videos.html", + res.types.Video( url=url, title=extract_text(eval_xpath(result, ".//h2[contains(@class, 'video-card-title')]")) or "", content=extract_text(eval_xpath(result, ".//p")) or "", diff --git a/searx/engines/qwant.py b/searx/engines/qwant.py index 182394777..a4fce2cf6 100644 --- a/searx/engines/qwant.py +++ b/searx/engines/qwant.py @@ -292,7 +292,7 @@ def response(resp: "SXNG_Response") -> EngineResults: thumbnail = thumbnail.replace("https://s2.qwant.com", "https://s1.qwant.com", 1) res.add( - res.types.LegacyResult( + res.types.Video( title=title, url=res_url, content=content, diff --git a/searx/engines/startpagina.py b/searx/engines/startpagina.py index 770990735..ca252dbfd 100644 --- a/searx/engines/startpagina.py +++ b/searx/engines/startpagina.py @@ -7,10 +7,9 @@ results from Google. import typing as t from urllib.parse import urlencode - +import datetime from dateutil import parser -from searx.utils import format_duration from searx.result_types import EngineResults if t.TYPE_CHECKING: @@ -49,7 +48,7 @@ def request(query: str, params: "OnlineParams") -> None: params["url"] = f"{api_url}/api/v2/search/{startpagina_categ}/?{urlencode(args)}" -def response(resp: "SXNG_Response"): +def response(resp: "SXNG_Response") -> EngineResults: res = EngineResults() json_resp = resp.json() @@ -81,13 +80,12 @@ def response(resp: "SXNG_Response"): ) elif startpagina_categ == "videos": res.add( - res.types.LegacyResult( - template="videos.html", + res.types.Video( url=result["original_url"], title=result["title"], content=result["description"], thumbnail=result["video"]["thumbnail_url"], - length=format_duration(result["video"]["duration"]), + length=datetime.timedelta(seconds=result["video"]["duration"]), ) ) elif startpagina_categ == "images": diff --git a/searx/engines/swisscows.py b/searx/engines/swisscows.py index 97208a08c..3eaa74dae 100644 --- a/searx/engines/swisscows.py +++ b/searx/engines/swisscows.py @@ -16,7 +16,7 @@ from urllib.parse import urlencode from babel.core import get_global -from searx.result_types import EngineResults, LegacyResult # pyright: ignore[reportPrivateLocalImportUsage] +from searx.result_types import EngineResults, Video from searx.utils import humanize_number, html_to_text if t.TYPE_CHECKING: @@ -215,28 +215,24 @@ def request(query: str, params: "OnlineParams") -> None: params["url"] = f"{base_url}{base_path}?{urlencode(args)}" -def _video_result(result: dict[str, str]) -> LegacyResult: +def _video_result(result: dict[str, str]) -> Video: published_date = None if result.get("datePublished"): published_date = datetime.fromisoformat(result["datePublished"]) - view_count = None + view_count = "" if result.get("viewCount"): view_count = humanize_number(result["viewCount"]) # pyright: ignore[reportArgumentType] - return LegacyResult( - { - "template": "videos.html", - "url": result["url"], - "title": html_to_text(result.get("title") or result["name"]), - "content": result["description"], - "thumbnail": result.get("thumbnailUrl") - or result.get("thumbnail", {}).get("url"), # pyright: ignore[reportAttributeAccessIssue] - "length": result.get("duration"), - "iframe_src": result.get("embedUrl"), - "publishedDate": published_date, - "views": view_count, - } + return Video( + url=result["url"], + title=html_to_text(result.get("title") or result["name"]), + content=result["description"], + thumbnail=result.get("thumbnailUrl") or result.get("thumbnail", {}).get("url"), + length=result.get("duration"), + iframe_src=result.get("embedUrl"), + publishedDate=published_date, + views=view_count, ) @@ -272,14 +268,11 @@ def response(resp: "SXNG_Response") -> EngineResults: res.add(_video_result(video)) elif result["type"] == "ImageObject": res.add( - res.types.LegacyResult( - { - "template": "images.html", - "url": result["url"], - "thumbnail_src": result["thumbnail"]["url"], - "img_src": result["contentUrl"], - "title": result["name"], - } + res.types.Image( + url=result["url"], + thumbnail_src=result["thumbnail"]["url"], + img_src=result["contentUrl"], + title=result["name"], ) ) elif result["type"] == "video": diff --git a/searx/engines/tagesschau.py b/searx/engines/tagesschau.py index 94561d42e..7961e73dd 100644 --- a/searx/engines/tagesschau.py +++ b/searx/engines/tagesschau.py @@ -20,6 +20,8 @@ from datetime import datetime from urllib.parse import urlencode import re +from searx.result_types import Video + about = { 'website': "https://tagesschau.de", 'wikidata_id': "Q703907", @@ -87,7 +89,7 @@ def _story(item): } -def _video(item): +def _video(item) -> Video: streams = item['streams'] video_url = streams.get('h264s') or streams.get('h264m') or streams.get('h264l') or streams.get('h264xl') title = item['title'] @@ -99,12 +101,11 @@ def _video(item): # sometimes, only adaptive m3u8 streams are available, so video_url is None url = video_url or f"{base_url}/multimedia/video/{item['sophoraId']}.html" - return { - 'template': 'videos.html', - 'title': title, - 'thumbnail': item.get('teaserImage', {}).get('imageVariants', {}).get('16x9-256'), - 'publishedDate': datetime.fromisoformat(item['date'][:19]), - 'content': item.get('firstSentence', ''), - 'iframe_src': video_url, - 'url': url, - } + return Video( + title=title, + thumbnail=item.get('teaserImage', {}).get('imageVariants', {}).get('16x9-256'), + publishedDate=datetime.fromisoformat(item['date'][:19]), + content=item.get('firstSentence', ''), + iframe_src=video_url, + url=url, + ) diff --git a/searx/engines/tonline.py b/searx/engines/tonline.py index 96e2f88f1..152f441e6 100644 --- a/searx/engines/tonline.py +++ b/searx/engines/tonline.py @@ -119,8 +119,7 @@ def _video_results(doc: ElementType, res: EngineResults): continue title_parts: list[ElementType] = eval_xpath(result, ".//a[starts-with(@class, 'tMMReshl')]") res.add( - res.types.LegacyResult( - template="videos.html", + res.types.Video( url=url, title=" - ".join(extract_text(part) or "" for part in title_parts), thumbnail=extract_text(eval_xpath(result, ".//img/@src") or "") or "", diff --git a/searx/engines/tusksearch.py b/searx/engines/tusksearch.py index 0639df516..dca81849e 100644 --- a/searx/engines/tusksearch.py +++ b/searx/engines/tusksearch.py @@ -143,8 +143,7 @@ def response(resp: "SXNG_Response"): pass res.add( - res.types.LegacyResult( - template="videos.html", + res.types.Video( url=result["url"], title=html_to_text(result["title"]), content=html_to_text(result["description"]), diff --git a/searx/engines/vuhuv.py b/searx/engines/vuhuv.py index 586edfe79..babf9f9ec 100644 --- a/searx/engines/vuhuv.py +++ b/searx/engines/vuhuv.py @@ -85,8 +85,7 @@ def _video_results(doc: "ElementBase") -> EngineResults: for result in eval_xpath_list(doc, "//div[contains(@class, 'item video')]"): ( res.add( - res.types.MainResult( - template="videos.html", + res.types.Video( url=extract_text(eval_xpath(result, "./a/@href")) or "", title=extract_text(eval_xpath(result, "./a/@title")) or "", content=extract_text(eval_xpath(result, ".//div[contains(@class, 'abaslik')]")) or "", diff --git a/searx/engines/wikicommons.py b/searx/engines/wikicommons.py index c838702e9..72a65cf65 100644 --- a/searx/engines/wikicommons.py +++ b/searx/engines/wikicommons.py @@ -181,8 +181,7 @@ def response(resp: "SXNG_Response") -> EngineResults: if wc_search_type == "video": res.add( - res.types.LegacyResult( - template="videos.html", + res.types.Video( title=title, url=url, content=content, diff --git a/searx/utils.py b/searx/utils.py index 3465ee361..8a30de909 100644 --- a/searx/utils.py +++ b/searx/utils.py @@ -2,7 +2,6 @@ """Utility functions for the engines""" from hashlib import pbkdf2_hmac -import time import re import importlib @@ -816,15 +815,6 @@ def parse_duration_string(duration_str: str) -> timedelta | None: return None -# Format the video duration -def format_duration(duration: str | int) -> str: - seconds = int(duration) - length = time.gmtime(seconds) - if length.tm_hour: - return time.strftime("%H:%M:%S", length) - return time.strftime("%M:%S", length) - - def _array_startswith(arr: bytes, prefix: bytes) -> bool: return arr[: len(prefix)] == prefix