diff --git a/searx/engines/duckduckgo_weather.py b/searx/engines/duckduckgo_weather.py
new file mode 100644
index 000000000..d0f34d60f
--- /dev/null
+++ b/searx/engines/duckduckgo_weather.py
@@ -0,0 +1,136 @@
+# SPDX-License-Identifier: AGPL-3.0-or-later
+# lint: pylint
+"""DuckDuckGo Weather"""
+
+from json import loads
+from urllib.parse import quote
+
+from datetime import datetime
+from flask_babel import gettext
+
+about = {
+ "website": 'https://duckduckgo.com/',
+ "wikidata_id": 'Q12805',
+ "official_api_documentation": None,
+ "use_official_api": True,
+ "require_api_key": False,
+ "results": "JSON",
+}
+
+categories = ["others"]
+
+url = "https://duckduckgo.com/js/spice/forecast/{query}/{lang}"
+
+
+def generate_condition_table(condition):
+ res = ""
+
+ res += f"
{gettext('Condition')} | " f"{condition['summary']} |
"
+
+ res += (
+ f"{gettext('Temperature')} | "
+ f"{f_to_c(condition['temperature'])}°C / {condition['temperature']}°F |
"
+ )
+
+ res += (
+ f"{gettext('Feels like')} | {f_to_c(condition['apparentTemperature'])}°C / "
+ f"{condition['apparentTemperature']}°F |
"
+ )
+
+ res += (
+ f"{gettext('Wind')} | {condition['windBearing']}° — "
+ f"{'%.2f' % (condition['windSpeed'] * 1.6093440006147)} km/h / {condition['windSpeed']} mph |
"
+ )
+
+ res += f"{gettext('Visibility')} | {condition['visibility']} km | "
+
+ res += f"
{gettext('Humidity')} | {condition['humidity'] * 100}% |
"
+
+ return res
+
+
+def generate_day_table(day):
+ res = ""
+
+ res += (
+ f"{gettext('Min temp.')} | {f_to_c(day['temperatureLow'])}°C / "
+ f"{day['temperatureLow']}°F |
"
+ )
+ res += (
+ f"{gettext('Max temp.')} | {f_to_c(day['temperatureHigh'])}°C / "
+ f"{day['temperatureHigh']}°F |
"
+ )
+ res += f"{gettext('UV index')} | {day['uvIndex']} |
"
+ res += (
+ f"{gettext('Sunrise')} | {datetime.fromtimestamp(day['sunriseTime']).strftime('%H:%M')} |
"
+ )
+ res += (
+ f"{gettext('Sunset')} | {datetime.fromtimestamp(day['sunsetTime']).strftime('%H:%M')} |
"
+ )
+
+ return res
+
+
+def request(query, params):
+ params["url"] = url.format(query=quote(query), lang=params['language'].split('-')[0])
+
+ return params
+
+
+def f_to_c(temperature):
+ return "%.2f" % ((temperature - 32) / 1.8)
+
+
+def response(resp):
+ results = []
+
+ if resp.text.strip() == "ddg_spice_forecast();":
+ return []
+
+ result = loads(resp.text[resp.text.find('\n') + 1 : resp.text.rfind('\n') - 2])
+
+ current = result["currently"]
+
+ title = result['flags']['ddg-location']
+
+ infobox = f"{gettext('Current condition')}
"
+
+ infobox += generate_condition_table(current)
+
+ infobox += "
"
+
+ last_date = None
+
+ for time in result['hourly']['data']:
+ current_time = datetime.fromtimestamp(time['time'])
+
+ if last_date != current_time.date():
+ if last_date is not None:
+ infobox += ""
+
+ infobox += f"{current_time.strftime('%Y-%m-%d')}
"
+
+ infobox += ""
+
+ for day in result['daily']['data']:
+ if datetime.fromtimestamp(day['time']).date() == current_time.date():
+ infobox += generate_day_table(day)
+
+ infobox += "
"
+
+ last_date = current_time.date()
+
+ infobox += f"{current_time.strftime('%H:%M')} |
"
+
+ infobox += generate_condition_table(time)
+
+ infobox += "
"
+
+ results.append(
+ {
+ "infobox": title,
+ "content": infobox,
+ }
+ )
+
+ return results
diff --git a/searx/settings.yml b/searx/settings.yml
index c97a86e4f..85bcec860 100644
--- a/searx/settings.yml
+++ b/searx/settings.yml
@@ -551,6 +551,11 @@ engines:
timeout: 3.0
disabled: true
+ - name: duckduckgo weather
+ engine: duckduckgo_weather
+ shortcut: ddw
+ disabled: true
+
- name: apple maps
engine: apple_maps
shortcut: apm