| 
									
										
										
										
											2021-01-13 11:31:25 +01:00
										 |  |  | # SPDX-License-Identifier: AGPL-3.0-or-later | 
					
						
							| 
									
										
										
										
											2017-05-21 05:33:08 +02:00
										 |  |  | """
 | 
					
						
							| 
									
										
										
										
											2023-10-07 10:26:04 +02:00
										 |  |  | DuckDuckGo Extra (images, videos, news) | 
					
						
							|  |  |  | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ | 
					
						
							| 
									
										
										
										
											2017-05-21 05:33:08 +02:00
										 |  |  | """
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-10-07 10:26:04 +02:00
										 |  |  | from datetime import datetime | 
					
						
							| 
									
										
										
										
											2022-11-05 15:10:52 +01:00
										 |  |  | from typing import TYPE_CHECKING | 
					
						
							| 
									
										
										
										
											2020-08-06 17:42:46 +02:00
										 |  |  | from urllib.parse import urlencode | 
					
						
							| 
									
										
										
										
											2024-09-15 00:28:35 +02:00
										 |  |  | from searx.utils import get_embeded_stream_url | 
					
						
							| 
									
										
										
										
											2022-11-05 15:10:52 +01:00
										 |  |  | 
 | 
					
						
							|  |  |  | from searx.engines.duckduckgo import fetch_traits  # pylint: disable=unused-import | 
					
						
							|  |  |  | from searx.engines.duckduckgo import ( | 
					
						
							|  |  |  |     get_ddg_lang, | 
					
						
							|  |  |  |     get_vqd, | 
					
						
							| 
									
										
										
										
											2021-12-27 10:16:20 +01:00
										 |  |  | ) | 
					
						
							| 
									
										
										
										
											2022-11-05 15:10:52 +01:00
										 |  |  | from searx.enginelib.traits import EngineTraits | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | if TYPE_CHECKING: | 
					
						
							|  |  |  |     import logging | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     logger: logging.Logger | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | traits: EngineTraits | 
					
						
							| 
									
										
										
										
											2017-05-21 05:33:08 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-01-13 11:31:25 +01:00
										 |  |  | # about | 
					
						
							|  |  |  | about = { | 
					
						
							|  |  |  |     "website": 'https://duckduckgo.com/', | 
					
						
							|  |  |  |     "wikidata_id": 'Q12805', | 
					
						
							|  |  |  |     "use_official_api": False, | 
					
						
							|  |  |  |     "require_api_key": False, | 
					
						
							|  |  |  |     "results": 'JSON (site requires js to get images)', | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-05-21 05:33:08 +02:00
										 |  |  | # engine dependent config | 
					
						
							| 
									
										
										
										
											2021-12-22 16:58:52 +01:00
										 |  |  | categories = ['images', 'web'] | 
					
						
							| 
									
										
										
										
											2023-10-07 10:26:04 +02:00
										 |  |  | ddg_category = 'images' | 
					
						
							|  |  |  | """The category must be any of ``images``, ``videos`` and ``news``
 | 
					
						
							|  |  |  | """
 | 
					
						
							| 
									
										
										
										
											2017-05-21 05:33:08 +02:00
										 |  |  | paging = True | 
					
						
							|  |  |  | safesearch = True | 
					
						
							| 
									
										
										
										
											2022-08-01 17:01:59 +02:00
										 |  |  | send_accept_language_header = True | 
					
						
							| 
									
										
										
										
											2017-05-21 05:33:08 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-11-05 15:10:52 +01:00
										 |  |  | safesearch_cookies = {0: '-2', 1: None, 2: '1'} | 
					
						
							|  |  |  | safesearch_args = {0: '1', 1: None, 2: '1'} | 
					
						
							| 
									
										
										
										
											2017-05-21 05:33:08 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-10-07 10:26:04 +02:00
										 |  |  | search_path_map = {'images': 'i', 'videos': 'v', 'news': 'news'} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-05-21 05:33:08 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-11-05 15:10:52 +01:00
										 |  |  | def request(query, params): | 
					
						
							| 
									
										
										
										
											2017-05-21 05:33:08 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-10-10 11:08:14 +02:00
										 |  |  |     # request needs a vqd argument | 
					
						
							|  |  |  |     vqd = get_vqd(query) | 
					
						
							|  |  |  |     if not vqd: | 
					
						
							|  |  |  |         # some search terms do not have results and therefore no vqd value | 
					
						
							|  |  |  |         params['url'] = None | 
					
						
							|  |  |  |         return params | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-11-05 15:10:52 +01:00
										 |  |  |     eng_region = traits.get_region(params['searxng_locale'], traits.all_locale) | 
					
						
							|  |  |  |     eng_lang = get_ddg_lang(traits, params['searxng_locale']) | 
					
						
							| 
									
										
										
										
											2017-05-21 05:33:08 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-11-05 15:10:52 +01:00
										 |  |  |     args = { | 
					
						
							|  |  |  |         'q': query, | 
					
						
							|  |  |  |         'o': 'json', | 
					
						
							|  |  |  |         # 'u': 'bing', | 
					
						
							|  |  |  |         'l': eng_region, | 
					
						
							| 
									
										
										
										
											2023-09-05 20:25:13 +02:00
										 |  |  |         'f': ',,,,,', | 
					
						
							| 
									
										
										
										
											2023-10-10 11:08:14 +02:00
										 |  |  |         'vqd': vqd, | 
					
						
							| 
									
										
										
										
											2022-11-05 15:10:52 +01:00
										 |  |  |     } | 
					
						
							| 
									
										
										
										
											2017-05-21 05:33:08 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-11-05 15:10:52 +01:00
										 |  |  |     if params['pageno'] > 1: | 
					
						
							|  |  |  |         args['s'] = (params['pageno'] - 1) * 100 | 
					
						
							| 
									
										
										
										
											2017-05-21 05:33:08 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-11-05 15:10:52 +01:00
										 |  |  |     params['cookies']['ad'] = eng_lang  # zh_CN | 
					
						
							|  |  |  |     params['cookies']['ah'] = eng_region  # "us-en,de-de" | 
					
						
							|  |  |  |     params['cookies']['l'] = eng_region  # "hk-tzh" | 
					
						
							| 
									
										
										
										
											2017-05-21 05:33:08 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-11-05 15:10:52 +01:00
										 |  |  |     safe_search = safesearch_cookies.get(params['safesearch']) | 
					
						
							|  |  |  |     if safe_search is not None: | 
					
						
							|  |  |  |         params['cookies']['p'] = safe_search  # "-2", "1" | 
					
						
							|  |  |  |     safe_search = safesearch_args.get(params['safesearch']) | 
					
						
							|  |  |  |     if safe_search is not None: | 
					
						
							|  |  |  |         args['p'] = safe_search  # "-1", "1" | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-09-05 20:25:13 +02:00
										 |  |  |     logger.debug("cookies: %s", params['cookies']) | 
					
						
							| 
									
										
										
										
											2023-10-07 10:26:04 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  |     params['url'] = f'https://duckduckgo.com/{search_path_map[ddg_category]}.js?{urlencode(args)}' | 
					
						
							| 
									
										
										
										
											2017-05-21 05:33:08 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  |     return params | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-10-07 10:26:04 +02:00
										 |  |  | def _image_result(result): | 
					
						
							|  |  |  |     return { | 
					
						
							|  |  |  |         'template': 'images.html', | 
					
						
							|  |  |  |         'url': result['url'], | 
					
						
							|  |  |  |         'title': result['title'], | 
					
						
							|  |  |  |         'content': '', | 
					
						
							|  |  |  |         'thumbnail_src': result['thumbnail'], | 
					
						
							|  |  |  |         'img_src': result['image'], | 
					
						
							| 
									
										
										
										
											2024-02-20 10:51:58 +01:00
										 |  |  |         'resolution': '%s x %s' % (result['width'], result['height']), | 
					
						
							| 
									
										
										
										
											2023-10-07 10:26:04 +02:00
										 |  |  |         'source': result['source'], | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | def _video_result(result): | 
					
						
							|  |  |  |     return { | 
					
						
							|  |  |  |         'template': 'videos.html', | 
					
						
							|  |  |  |         'url': result['content'], | 
					
						
							|  |  |  |         'title': result['title'], | 
					
						
							|  |  |  |         'content': result['description'], | 
					
						
							|  |  |  |         'thumbnail': result['images'].get('small') or result['images'].get('medium'), | 
					
						
							| 
									
										
										
										
											2024-09-15 00:28:35 +02:00
										 |  |  |         'iframe_src': get_embeded_stream_url(result['content']), | 
					
						
							| 
									
										
										
										
											2023-10-07 10:26:04 +02:00
										 |  |  |         'source': result['provider'], | 
					
						
							|  |  |  |         'length': result['duration'], | 
					
						
							|  |  |  |         'metadata': result.get('uploader'), | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | def _news_result(result): | 
					
						
							|  |  |  |     return { | 
					
						
							|  |  |  |         'url': result['url'], | 
					
						
							|  |  |  |         'title': result['title'], | 
					
						
							|  |  |  |         'content': result['excerpt'], | 
					
						
							|  |  |  |         'source': result['source'], | 
					
						
							|  |  |  |         'publishedDate': datetime.utcfromtimestamp(result['date']), | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-05-21 05:33:08 +02:00
										 |  |  | def response(resp): | 
					
						
							|  |  |  |     results = [] | 
					
						
							| 
									
										
										
										
											2022-11-05 15:10:52 +01:00
										 |  |  |     res_json = resp.json() | 
					
						
							| 
									
										
										
										
											2017-05-21 05:33:08 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  |     for result in res_json['results']: | 
					
						
							| 
									
										
										
										
											2023-10-07 10:26:04 +02:00
										 |  |  |         if ddg_category == 'images': | 
					
						
							|  |  |  |             results.append(_image_result(result)) | 
					
						
							|  |  |  |         elif ddg_category == 'videos': | 
					
						
							|  |  |  |             results.append(_video_result(result)) | 
					
						
							|  |  |  |         elif ddg_category == 'news': | 
					
						
							|  |  |  |             results.append(_news_result(result)) | 
					
						
							|  |  |  |         else: | 
					
						
							|  |  |  |             raise ValueError(f"Invalid duckduckgo category: {ddg_category}") | 
					
						
							| 
									
										
										
										
											2017-05-21 05:33:08 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  |     return results |