odysee.py 4.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145
  1. # SPDX-License-Identifier: AGPL-3.0-or-later
  2. # lint: pylint
  3. """Odysee_ is a decentralized video hosting platform.
  4. .. _Odysee: https://github.com/OdyseeTeam/odysee-frontend
  5. """
  6. import time
  7. from urllib.parse import urlencode
  8. from datetime import datetime
  9. import babel
  10. from searx.network import get
  11. from searx.locales import language_tag
  12. from searx.enginelib.traits import EngineTraits
  13. traits: EngineTraits
  14. # Engine metadata
  15. about = {
  16. "website": "https://odysee.com/",
  17. "wikidata_id": "Q102046570",
  18. "official_api_documentation": None,
  19. "use_official_api": False,
  20. "require_api_key": False,
  21. "results": "JSON",
  22. }
  23. # Engine configuration
  24. paging = True
  25. time_range_support = True
  26. results_per_page = 20
  27. categories = ['videos']
  28. # Search URL (Note: lighthouse.lbry.com/search works too, and may be faster at times)
  29. base_url = "https://lighthouse.odysee.tv/search"
  30. def request(query, params):
  31. time_range_dict = {
  32. "day": "today",
  33. "week": "thisweek",
  34. "month": "thismonth",
  35. "year": "thisyear",
  36. }
  37. start_index = (params["pageno"] - 1) * results_per_page
  38. query_params = {
  39. "s": query,
  40. "size": results_per_page,
  41. "from": start_index,
  42. "include": "channel,thumbnail_url,title,description,duration,release_time",
  43. "mediaType": "video",
  44. }
  45. lang = traits.get_language(params['searxng_locale'], None)
  46. if lang is not None:
  47. query_params['language'] = lang
  48. if params['time_range'] in time_range_dict:
  49. query_params['time_filter'] = time_range_dict[params['time_range']]
  50. params["url"] = f"{base_url}?{urlencode(query_params)}"
  51. return params
  52. # Format the video duration
  53. def format_duration(duration):
  54. seconds = int(duration)
  55. length = time.gmtime(seconds)
  56. if length.tm_hour:
  57. return time.strftime("%H:%M:%S", length)
  58. return time.strftime("%M:%S", length)
  59. def response(resp):
  60. data = resp.json()
  61. results = []
  62. for item in data:
  63. name = item["name"]
  64. claim_id = item["claimId"]
  65. title = item["title"]
  66. thumbnail_url = item["thumbnail_url"]
  67. description = item["description"] or ""
  68. channel = item["channel"]
  69. release_time = item["release_time"]
  70. duration = item["duration"]
  71. release_date = datetime.strptime(release_time.split("T")[0], "%Y-%m-%d")
  72. formatted_date = datetime.utcfromtimestamp(release_date.timestamp())
  73. url = f"https://odysee.com/{name}:{claim_id}"
  74. iframe_url = f"https://odysee.com/$/embed/{name}:{claim_id}"
  75. odysee_thumbnail = f"https://thumbnails.odycdn.com/optimize/s:390:0/quality:85/plain/{thumbnail_url}"
  76. formatted_duration = format_duration(duration)
  77. results.append(
  78. {
  79. "title": title,
  80. "url": url,
  81. "content": description,
  82. "author": channel,
  83. "publishedDate": formatted_date,
  84. "length": formatted_duration,
  85. "thumbnail": odysee_thumbnail,
  86. "iframe_src": iframe_url,
  87. "template": "videos.html",
  88. }
  89. )
  90. return results
  91. def fetch_traits(engine_traits: EngineTraits):
  92. """
  93. Fetch languages from Odysee's source code.
  94. """
  95. resp = get(
  96. 'https://raw.githubusercontent.com/OdyseeTeam/odysee-frontend/master/ui/constants/supported_browser_languages.js', # pylint: disable=line-too-long
  97. timeout=60,
  98. )
  99. if not resp.ok:
  100. print("ERROR: can't determine languages from Odysee")
  101. return
  102. for line in resp.text.split("\n")[1:-4]:
  103. lang_tag = line.strip().split(": ")[0].replace("'", "")
  104. try:
  105. sxng_tag = language_tag(babel.Locale.parse(lang_tag, sep="-"))
  106. except babel.UnknownLocaleError:
  107. print("ERROR: %s is unknown by babel" % lang_tag)
  108. continue
  109. conflict = engine_traits.languages.get(sxng_tag)
  110. if conflict:
  111. if conflict != lang_tag:
  112. print("CONFLICT: babel %s --> %s, %s" % (sxng_tag, conflict, lang_tag))
  113. continue
  114. engine_traits.languages[sxng_tag] = lang_tag