tools.py 6.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155
  1. # -*- encoding: utf-8 -*-
  2. # Part of Odoo. See LICENSE file for full copyright and licensing details.
  3. import base64
  4. import re
  5. import requests
  6. from markupsafe import Markup
  7. from werkzeug.urls import url_encode
  8. from odoo import _
  9. from odoo.tools import image_process
  10. # To detect if we have a valid URL or not
  11. valid_url_regex = r'^(http://|https://|//)[a-z0-9]+([\-\.]{1}[a-z0-9]+)*\.[a-z]{2,5}(:[0-9]{1,5})?(/.*)?$'
  12. # Regex for few of the widely used video hosting services
  13. player_regexes = {
  14. 'youtube': r'^(?:(?:https?:)?//)?(?:www\.)?(?:youtu\.be/|youtube(-nocookie)?\.com/(?:embed/|v/|watch\?v=|watch\?.+&v=))((?:\w|-){11})\S*$',
  15. 'vimeo': r'//(player.)?vimeo.com/([a-z]*/)*([0-9]{6,11})[?]?.*',
  16. 'dailymotion': r'(https?:\/\/)(www\.)?(dailymotion\.com\/(embed\/video\/|embed\/|video\/|hub\/.*#video=)|dai\.ly\/)(?P<id>[A-Za-z0-9]{6,7})',
  17. 'instagram': r'(?:(.*)instagram.com|instagr\.am)/p/(.[a-zA-Z0-9-_\.]*)',
  18. 'youku': r'(?:(https?:\/\/)?(v\.youku\.com/v_show/id_|player\.youku\.com/player\.php/sid/|player\.youku\.com/embed/|cloud\.youku\.com/services/sharev\?vid=|video\.tudou\.com/v/)|youku:)(?P<id>[A-Za-z0-9]+)(?:\.html|/v\.swf|)',
  19. }
  20. def get_video_source_data(video_url):
  21. """ Computes the valid source, document ID and regex match from given URL
  22. (or None in case of invalid URL).
  23. """
  24. if not video_url:
  25. return None
  26. if re.search(valid_url_regex, video_url):
  27. youtube_match = re.search(player_regexes['youtube'], video_url)
  28. if youtube_match:
  29. return ('youtube', youtube_match[2], youtube_match)
  30. vimeo_match = re.search(player_regexes['vimeo'], video_url)
  31. if vimeo_match:
  32. return ('vimeo', vimeo_match[3], vimeo_match)
  33. dailymotion_match = re.search(player_regexes['dailymotion'], video_url)
  34. if dailymotion_match:
  35. return ('dailymotion', dailymotion_match.group("id"), dailymotion_match)
  36. instagram_match = re.search(player_regexes['instagram'], video_url)
  37. if instagram_match:
  38. return ('instagram', instagram_match[2], instagram_match)
  39. youku_match = re.search(player_regexes['youku'], video_url)
  40. if youku_match:
  41. return ('youku', youku_match.group("id"), youku_match)
  42. return None
  43. def get_video_url_data(video_url, autoplay=False, loop=False, hide_controls=False, hide_fullscreen=False, hide_yt_logo=False, hide_dm_logo=False, hide_dm_share=False):
  44. """ Computes the platform name and embed_url from given URL
  45. (or error message in case of invalid URL).
  46. """
  47. source = get_video_source_data(video_url)
  48. if source is None:
  49. return {'error': True, 'message': _('The provided url is invalid')}
  50. embed_url = video_url
  51. platform, video_id, platform_match = source
  52. params = {}
  53. if platform == 'youtube':
  54. params['rel'] = 0
  55. params['autoplay'] = autoplay and 1 or 0
  56. if autoplay:
  57. params['mute'] = 1
  58. # The youtube js api is needed for autoplay on mobile. Note: this
  59. # was added as a fix, old customers may have autoplay videos
  60. # without this, which will make their video autoplay on desktop but
  61. # not in mobile (so no behavior change was done in stable, this
  62. # should not be migrated).
  63. params['enablejsapi'] = 1
  64. if hide_controls:
  65. params['controls'] = 0
  66. if loop:
  67. params['loop'] = 1
  68. params['playlist'] = video_id
  69. if hide_fullscreen:
  70. params['fs'] = 0
  71. if hide_yt_logo:
  72. params['modestbranding'] = 1
  73. yt_extra = platform_match[1] or ''
  74. embed_url = f'//www.youtube{yt_extra}.com/embed/{video_id}'
  75. elif platform == 'vimeo':
  76. params['autoplay'] = autoplay and 1 or 0
  77. if autoplay:
  78. params['muted'] = 1
  79. params['autopause'] = 0
  80. if hide_controls:
  81. params['controls'] = 0
  82. if loop:
  83. params['loop'] = 1
  84. embed_url = f'//player.vimeo.com/video/{video_id}'
  85. elif platform == 'dailymotion':
  86. params['autoplay'] = autoplay and 1 or 0
  87. if autoplay:
  88. params['mute'] = 1
  89. if hide_controls:
  90. params['controls'] = 0
  91. if hide_dm_logo:
  92. params['ui-logo'] = 0
  93. if hide_dm_share:
  94. params['sharing-enable'] = 0
  95. embed_url = f'//www.dailymotion.com/embed/video/{video_id}'
  96. elif platform == 'instagram':
  97. embed_url = f'//www.instagram.com/p/{video_id}/embed/'
  98. elif platform == 'youku':
  99. embed_url = f'//player.youku.com/embed/{video_id}'
  100. if params:
  101. embed_url = f'{embed_url}?{url_encode(params)}'
  102. return {'platform': platform, 'embed_url': embed_url}
  103. def get_video_embed_code(video_url):
  104. """ Computes the valid iframe from given URL that can be embedded
  105. (or None in case of invalid URL).
  106. """
  107. data = get_video_url_data(video_url)
  108. if 'error' in data:
  109. return None
  110. return Markup('<iframe class="embed-responsive-item" src="%s" allow="accelerometer; autoplay; encrypted-media; gyroscope; picture-in-picture" allowFullScreen="true" frameborder="0"></iframe>') % data['embed_url']
  111. def get_video_thumbnail(video_url):
  112. """ Computes the valid thumbnail image from given URL
  113. (or None in case of invalid URL).
  114. """
  115. source = get_video_source_data(video_url)
  116. if source is None:
  117. return None
  118. response = None
  119. platform, video_id = source[:2]
  120. if platform == 'youtube':
  121. response = requests.get(f'https://img.youtube.com/vi/{video_id}/0.jpg', timeout=10)
  122. elif platform == 'vimeo':
  123. res = requests.get(f'http://vimeo.com/api/oembed.json?url={video_url}', timeout=10)
  124. if res.ok:
  125. data = res.json()
  126. response = requests.get(data['thumbnail_url'], timeout=10)
  127. elif platform == 'dailymotion':
  128. response = requests.get(f'https://www.dailymotion.com/thumbnail/video/{video_id}', timeout=10)
  129. elif platform == 'instagram':
  130. response = requests.get(f'https://www.instagram.com/p/{video_id}/media/?size=t', timeout=10)
  131. if response and response.ok:
  132. return image_process(response.content)
  133. return None