3 Commits

Author SHA1 Message Date
Bnyro
4dfc47584d [refactor] duration strings: move parsing logic to utils.py 2025-03-25 16:48:44 +01:00
Bnyro
c28d35c7fc [fix] duckduckgo news: unescaped html sequences in description 2025-03-25 16:14:36 +01:00
dependabot[bot]
2ad987c711 Bump vite from 6.2.2 to 6.2.3 in /client/simple
Bumps [vite](https://github.com/vitejs/vite/tree/HEAD/packages/vite) from 6.2.2 to 6.2.3.
- [Release notes](https://github.com/vitejs/vite/releases)
- [Changelog](https://github.com/vitejs/vite/blob/v6.2.3/packages/vite/CHANGELOG.md)
- [Commits](https://github.com/vitejs/vite/commits/v6.2.3/packages/vite)

---
updated-dependencies:
- dependency-name: vite
  dependency-type: direct:development
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-03-25 16:04:41 +01:00
8 changed files with 49 additions and 49 deletions

View File

@@ -30,7 +30,7 @@
"stylelint-config-standard-less": "^3.0.1", "stylelint-config-standard-less": "^3.0.1",
"stylelint-prettier": "^5.0.3", "stylelint-prettier": "^5.0.3",
"svgo": "^3.3.2", "svgo": "^3.3.2",
"vite": "^6.2.2", "vite": "^6.2.3",
"vite-plugin-static-copy": "^2.3.0", "vite-plugin-static-copy": "^2.3.0",
"vite-plugin-stylelint": "^6.0.0", "vite-plugin-stylelint": "^6.0.0",
"webpack": "^5.97.1", "webpack": "^5.97.1",
@@ -6766,9 +6766,9 @@
"license": "MIT" "license": "MIT"
}, },
"node_modules/vite": { "node_modules/vite": {
"version": "6.2.2", "version": "6.2.3",
"resolved": "https://registry.npmjs.org/vite/-/vite-6.2.2.tgz", "resolved": "https://registry.npmjs.org/vite/-/vite-6.2.3.tgz",
"integrity": "sha512-yW7PeMM+LkDzc7CgJuRLMW2Jz0FxMOsVJ8Lv3gpgW9WLcb9cTW+121UEr1hvmfR7w3SegR5ItvYyzVz1vxNJgQ==", "integrity": "sha512-IzwM54g4y9JA/xAeBPNaDXiBF8Jsgl3VBQ2YQ/wOY6fyW3xMdSoltIV3Bo59DErdqdE6RxUfv8W69DvUorE4Eg==",
"dev": true, "dev": true,
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {

View File

@@ -28,7 +28,7 @@
"stylelint-config-standard-less": "^3.0.1", "stylelint-config-standard-less": "^3.0.1",
"stylelint-prettier": "^5.0.3", "stylelint-prettier": "^5.0.3",
"svgo": "^3.3.2", "svgo": "^3.3.2",
"vite": "^6.2.2", "vite": "^6.2.3",
"vite-plugin-static-copy": "^2.3.0", "vite-plugin-static-copy": "^2.3.0",
"vite-plugin-stylelint": "^6.0.0", "vite-plugin-stylelint": "^6.0.0",
"webpack": "^5.97.1", "webpack": "^5.97.1",

View File

@@ -56,18 +56,6 @@ def request(query, params):
return params return params
# Format the video duration
def format_duration(duration):
if not ":" in duration:
return None
minutes, seconds = map(int, duration.split(":"))
total_seconds = minutes * 60 + seconds
formatted_duration = str(timedelta(seconds=total_seconds))[2:] if 0 <= total_seconds < 3600 else ""
return formatted_duration
def response(resp): def response(resp):
search_res = resp.json() search_res = resp.json()
@@ -83,7 +71,12 @@ def response(resp):
unix_date = item["pubdate"] unix_date = item["pubdate"]
formatted_date = datetime.fromtimestamp(unix_date) formatted_date = datetime.fromtimestamp(unix_date)
formatted_duration = format_duration(item["duration"])
# the duration only seems to be valid if the video is less than 60 mins
duration = utils.parse_duration_string(item["duration"])
if duration and duration > timedelta(minutes=60):
duration = None
iframe_url = f"https://player.bilibili.com/player.html?aid={video_id}&high_quality=1&autoplay=false&danmaku=0" iframe_url = f"https://player.bilibili.com/player.html?aid={video_id}&high_quality=1&autoplay=false&danmaku=0"
results.append( results.append(
@@ -93,7 +86,7 @@ def response(resp):
"content": description, "content": description,
"author": author, "author": author,
"publishedDate": formatted_date, "publishedDate": formatted_date,
"length": formatted_duration, "length": duration,
"thumbnail": thumbnail, "thumbnail": thumbnail,
"iframe_src": iframe_url, "iframe_src": iframe_url,
"template": "videos.html", "template": "videos.html",

View File

@@ -9,7 +9,7 @@ from __future__ import annotations
from datetime import datetime from datetime import datetime
from typing import TYPE_CHECKING from typing import TYPE_CHECKING
from urllib.parse import urlencode from urllib.parse import urlencode
from searx.utils import get_embeded_stream_url from searx.utils import get_embeded_stream_url, html_to_text
from searx.engines.duckduckgo import fetch_traits # pylint: disable=unused-import from searx.engines.duckduckgo import fetch_traits # pylint: disable=unused-import
from searx.engines.duckduckgo import get_ddg_lang, get_vqd from searx.engines.duckduckgo import get_ddg_lang, get_vqd
@@ -126,7 +126,7 @@ def _news_result(result):
return { return {
'url': result['url'], 'url': result['url'],
'title': result['title'], 'title': result['title'],
'content': result['excerpt'], 'content': html_to_text(result['excerpt']),
'source': result['source'], 'source': result['source'],
'publishedDate': datetime.fromtimestamp(result['date']), 'publishedDate': datetime.fromtimestamp(result['date']),
} }

View File

@@ -2,9 +2,10 @@
"""iQiyi: A search engine for retrieving videos from iQiyi.""" """iQiyi: A search engine for retrieving videos from iQiyi."""
from urllib.parse import urlencode from urllib.parse import urlencode
from datetime import datetime, timedelta from datetime import datetime
from searx.exceptions import SearxEngineAPIException from searx.exceptions import SearxEngineAPIException
from searx.utils import parse_duration_string
about = { about = {
"website": "https://www.iqiyi.com/", "website": "https://www.iqiyi.com/",
@@ -55,20 +56,7 @@ def response(resp):
except (ValueError, TypeError): except (ValueError, TypeError):
pass pass
length = None length = parse_duration_string(album_info.get("subscriptionContent"))
subscript_content = album_info.get("subscriptContent")
if subscript_content:
try:
time_parts = subscript_content.split(":")
if len(time_parts) == 2:
minutes, seconds = map(int, time_parts)
length = timedelta(minutes=minutes, seconds=seconds)
elif len(time_parts) == 3:
hours, minutes, seconds = map(int, time_parts)
length = timedelta(hours=hours, minutes=minutes, seconds=seconds)
except (ValueError, TypeError):
pass
results.append( results.append(
{ {
'url': album_info.get("pageUrl", "").replace("http://", "https://"), 'url': album_info.get("pageUrl", "").replace("http://", "https://"),

View File

@@ -6,7 +6,7 @@
import re import re
from urllib.parse import urlencode from urllib.parse import urlencode
from datetime import datetime from datetime import datetime, timedelta
from dateutil.parser import parse from dateutil.parser import parse
from dateutil.relativedelta import relativedelta from dateutil.relativedelta import relativedelta
@@ -50,12 +50,6 @@ safesearch = True
safesearch_table = {0: 'both', 1: 'false', 2: 'false'} safesearch_table = {0: 'both', 1: 'false', 2: 'false'}
def minute_to_hm(minute):
if isinstance(minute, int):
return "%d:%02d" % (divmod(minute, 60))
return None
def request(query, params): def request(query, params):
"""Assemble request for the Peertube API""" """Assemble request for the Peertube API"""
@@ -117,13 +111,17 @@ def video_response(resp):
if x if x
] ]
duration = result.get('duration')
if duration:
duration = timedelta(seconds=duration)
results.append( results.append(
{ {
'url': result['url'], 'url': result['url'],
'title': result['name'], 'title': result['name'],
'content': html_to_text(result.get('description') or ''), 'content': html_to_text(result.get('description') or ''),
'author': result.get('account', {}).get('displayName'), 'author': result.get('account', {}).get('displayName'),
'length': minute_to_hm(result.get('duration')), 'length': duration,
'views': humanize_number(result['views']), 'views': humanize_number(result['views']),
'template': 'videos.html', 'template': 'videos.html',
'publishedDate': parse(result['publishedAt']), 'publishedDate': parse(result['publishedAt']),

View File

@@ -73,7 +73,7 @@ Implementations
from urllib.parse import urlencode, urlparse from urllib.parse import urlencode, urlparse
from searx import locales from searx import locales
from searx.network import get from searx.network import get
from searx.utils import gen_useragent, html_to_text from searx.utils import gen_useragent, html_to_text, parse_duration_string
about = { about = {
"website": "https://presearch.io", "website": "https://presearch.io",
@@ -270,7 +270,7 @@ def response(resp):
'url': item.get('link'), 'url': item.get('link'),
'content': item.get('description', ''), 'content': item.get('description', ''),
'thumbnail': item.get('image'), 'thumbnail': item.get('image'),
'length': item.get('duration'), 'length': parse_duration_string(item.get('duration')),
} }
) )

View File

@@ -1,7 +1,5 @@
# SPDX-License-Identifier: AGPL-3.0-or-later # SPDX-License-Identifier: AGPL-3.0-or-later
"""Utility functions for the engines """Utility functions for the engines"""
"""
from __future__ import annotations from __future__ import annotations
@@ -18,6 +16,7 @@ from random import choice
from html.parser import HTMLParser from html.parser import HTMLParser
from html import escape from html import escape
from urllib.parse import urljoin, urlparse, parse_qs, urlencode from urllib.parse import urljoin, urlparse, parse_qs, urlencode
from datetime import timedelta
from markdown_it import MarkdownIt from markdown_it import MarkdownIt
from lxml import html from lxml import html
@@ -831,3 +830,25 @@ def js_variable_to_python(js_variable):
s = s.replace(chr(1), ':') s = s.replace(chr(1), ':')
# load the JSON and return the result # load the JSON and return the result
return json.loads(s) return json.loads(s)
def parse_duration_string(duration_str: str) -> timedelta | None:
"""Parse a time string in format MM:SS or HH:MM:SS and convert it to a `timedelta` object.
Returns None if the provided string doesn't match any of the formats.
"""
duration_str = duration_str.strip()
if not duration_str:
return None
try:
# prepending ["00"] here inits hours to 0 if they are not provided
time_parts = (["00"] + duration_str.split(":"))[:3]
hours, minutes, seconds = map(int, time_parts)
return timedelta(hours=hours, minutes=minutes, seconds=seconds)
except (ValueError, TypeError):
pass
return None