dailymotion.py 5.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212
  1. # SPDX-License-Identifier: AGPL-3.0-or-later
  2. """Dailymotion (Videos)
  3. """
  4. from typing import Set
  5. from datetime import datetime, timedelta
  6. from urllib.parse import urlencode
  7. import time
  8. import babel
  9. from searx.exceptions import SearxEngineAPIException
  10. from searx import network
  11. from searx.utils import html_to_text
  12. from searx.enginelib.traits import EngineTraits
  13. # about
  14. about = {
  15. "website": 'https://www.dailymotion.com',
  16. "wikidata_id": 'Q769222',
  17. "official_api_documentation": 'https://www.dailymotion.com/developer',
  18. "use_official_api": True,
  19. "require_api_key": False,
  20. "results": 'JSON',
  21. }
  22. # engine dependent config
  23. categories = ['videos']
  24. paging = True
  25. number_of_results = 10
  26. time_range_support = True
  27. time_delta_dict = {
  28. "day": timedelta(days=1),
  29. "week": timedelta(days=7),
  30. "month": timedelta(days=31),
  31. "year": timedelta(days=365),
  32. }
  33. safesearch = True
  34. safesearch_params = {2: '&is_created_for_kids=true', 1: '&is_created_for_kids=true', 0: ''}
  35. # search-url
  36. # - https://developers.dailymotion.com/tools/
  37. # - https://www.dailymotion.com/doc/api/obj-video.html
  38. result_fields = [
  39. 'allow_embed',
  40. 'description',
  41. 'title',
  42. 'created_time',
  43. 'duration',
  44. 'url',
  45. 'thumbnail_360_url',
  46. 'id',
  47. ]
  48. search_url = (
  49. 'https://api.dailymotion.com/videos?'
  50. 'fields={fields}&password_protected={password_protected}&private={private}&sort={sort}&limit={limit}'
  51. ).format(
  52. fields=','.join(result_fields),
  53. password_protected='false',
  54. private='false',
  55. sort='relevance',
  56. limit=number_of_results,
  57. )
  58. iframe_src = "https://www.dailymotion.com/embed/video/{video_id}"
  59. # The request query filters by 'languages' & 'country', therefore instead of
  60. # fetching only languages we need to fetch locales.
  61. supported_languages_url = 'https://api.dailymotion.com/locales'
  62. supported_languages_iso639: Set[str] = set()
  63. def init(_engine_settings):
  64. global supported_languages_iso639
  65. supported_languages_iso639 = set([language.split('_')[0] for language in supported_languages])
  66. def request(query, params):
  67. if not query:
  68. return False
  69. language = params['language']
  70. if language == 'all':
  71. language = 'en-US'
  72. locale = babel.Locale.parse(language, sep='-')
  73. language_iso639 = locale.language
  74. if locale.language not in supported_languages_iso639:
  75. language_iso639 = 'en'
  76. query_args = {
  77. 'search': query,
  78. 'languages': language_iso639,
  79. 'page': params['pageno'],
  80. }
  81. if locale.territory:
  82. localization = locale.language + '_' + locale.territory
  83. if localization in supported_languages:
  84. query_args['country'] = locale.territory
  85. time_delta = time_delta_dict.get(params["time_range"])
  86. if time_delta:
  87. created_after = datetime.now() - time_delta
  88. query_args['created_after'] = datetime.timestamp(created_after)
  89. query_str = urlencode(query_args)
  90. params['url'] = search_url + '&' + query_str + safesearch_params.get(params['safesearch'], '')
  91. params['raise_for_httperror'] = False
  92. return params
  93. # get response from search-request
  94. def response(resp):
  95. results = []
  96. search_res = resp.json()
  97. # check for an API error
  98. if 'error' in search_res:
  99. raise SearxEngineAPIException(search_res['error'].get('message'))
  100. network.raise_for_httperror(resp)
  101. # parse results
  102. for res in search_res.get('list', []):
  103. title = res['title']
  104. url = res['url']
  105. content = html_to_text(res['description'])
  106. if len(content) > 300:
  107. content = content[:300] + '...'
  108. publishedDate = datetime.fromtimestamp(res['created_time'], None)
  109. length = time.gmtime(res.get('duration'))
  110. if length.tm_hour:
  111. length = time.strftime("%H:%M:%S", length)
  112. else:
  113. length = time.strftime("%M:%S", length)
  114. thumbnail = res['thumbnail_360_url']
  115. thumbnail = thumbnail.replace("http://", "https://")
  116. item = {
  117. 'template': 'videos.html',
  118. 'url': url,
  119. 'title': title,
  120. 'content': content,
  121. 'publishedDate': publishedDate,
  122. 'length': length,
  123. 'thumbnail': thumbnail,
  124. }
  125. # HINT: no mater what the value is, without API token videos can't shown
  126. # embedded
  127. if res['allow_embed']:
  128. item['iframe_src'] = iframe_src.format(video_id=res['id'])
  129. results.append(item)
  130. # return results
  131. return results
  132. # get supported languages from their site
  133. def _fetch_supported_languages(resp):
  134. response_json = resp.json()
  135. return [item['locale'] for item in response_json['list']]
  136. def fetch_traits(engine_traits: EngineTraits):
  137. """Fetch regions from dailymotion.
  138. There are duplications in the locale codes returned from Dailymotion which
  139. can be ignored::
  140. en_EN --> en_GB, en_US
  141. ar_AA --> ar_EG, ar_AE, ar_SA
  142. """
  143. # pylint: disable=import-outside-toplevel
  144. engine_traits.data_type = 'supported_languages' # deprecated
  145. from searx.locales import region_tag
  146. resp = network.get('https://api.dailymotion.com/locales')
  147. if not resp.ok:
  148. print("ERROR: response from peertube is not OK.")
  149. for item in resp.json()['list']:
  150. eng_tag = item['locale']
  151. if eng_tag in ('en_EN', 'ar_AA'):
  152. continue
  153. try:
  154. sxng_tag = region_tag(babel.Locale.parse(eng_tag))
  155. except babel.UnknownLocaleError:
  156. print("ERROR: item unknown --> %s" % item)
  157. continue
  158. conflict = engine_traits.regions.get(sxng_tag)
  159. if conflict:
  160. if conflict != eng_tag:
  161. print("CONFLICT: babel %s --> %s, %s" % (sxng_tag, conflict, eng_tag))
  162. continue
  163. engine_traits.regions[sxng_tag] = eng_tag