You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

139 lines
4.8 KiB

  1. # coding: utf-8
  2. from __future__ import unicode_literals
  3. from .common import InfoExtractor
  4. from ..compat import compat_HTTPError
  5. from ..utils import (
  6. float_or_none,
  7. int_or_none,
  8. try_get,
  9. # unified_timestamp,
  10. ExtractorError,
  11. )
  12. class RedBullTVIE(InfoExtractor):
  13. _VALID_URL = r'https?://(?:www\.)?redbull\.tv/(?:video|film|live)/(?:AP-\w+/segment/)?(?P<id>AP-\w+)'
  14. _TESTS = [{
  15. # film
  16. 'url': 'https://www.redbull.tv/video/AP-1Q756YYX51W11/abc-of-wrc',
  17. 'md5': 'fb0445b98aa4394e504b413d98031d1f',
  18. 'info_dict': {
  19. 'id': 'AP-1Q756YYX51W11',
  20. 'ext': 'mp4',
  21. 'title': 'ABC of...WRC',
  22. 'description': 'md5:5c7ed8f4015c8492ecf64b6ab31e7d31',
  23. 'duration': 1582.04,
  24. # 'timestamp': 1488405786,
  25. # 'upload_date': '20170301',
  26. },
  27. }, {
  28. # episode
  29. 'url': 'https://www.redbull.tv/video/AP-1PMT5JCWH1W11/grime?playlist=shows:shows-playall:web',
  30. 'info_dict': {
  31. 'id': 'AP-1PMT5JCWH1W11',
  32. 'ext': 'mp4',
  33. 'title': 'Grime - Hashtags S2 E4',
  34. 'description': 'md5:334b741c8c1ce65be057eab6773c1cf5',
  35. 'duration': 904.6,
  36. # 'timestamp': 1487290093,
  37. # 'upload_date': '20170217',
  38. 'series': 'Hashtags',
  39. 'season_number': 2,
  40. 'episode_number': 4,
  41. },
  42. 'params': {
  43. 'skip_download': True,
  44. },
  45. }, {
  46. # segment
  47. 'url': 'https://www.redbull.tv/live/AP-1R5DX49XS1W11/segment/AP-1QSAQJ6V52111/semi-finals',
  48. 'info_dict': {
  49. 'id': 'AP-1QSAQJ6V52111',
  50. 'ext': 'mp4',
  51. 'title': 'Semi Finals - Vans Park Series Pro Tour',
  52. 'description': 'md5:306a2783cdafa9e65e39aa62f514fd97',
  53. 'duration': 11791.991,
  54. },
  55. 'params': {
  56. 'skip_download': True,
  57. },
  58. }, {
  59. 'url': 'https://www.redbull.tv/film/AP-1MSKKF5T92111/in-motion',
  60. 'only_matching': True,
  61. }]
  62. def _real_extract(self, url):
  63. video_id = self._match_id(url)
  64. session = self._download_json(
  65. 'https://api-v2.redbull.tv/session', video_id,
  66. note='Downloading access token', query={
  67. 'build': '4.370.0',
  68. 'category': 'personal_computer',
  69. 'os_version': '1.0',
  70. 'os_family': 'http',
  71. })
  72. if session.get('code') == 'error':
  73. raise ExtractorError('%s said: %s' % (
  74. self.IE_NAME, session['message']))
  75. auth = '%s %s' % (session.get('token_type', 'Bearer'), session['access_token'])
  76. try:
  77. info = self._download_json(
  78. 'https://api-v2.redbull.tv/content/%s' % video_id,
  79. video_id, note='Downloading video information',
  80. headers={'Authorization': auth}
  81. )
  82. except ExtractorError as e:
  83. if isinstance(e.cause, compat_HTTPError) and e.cause.code == 404:
  84. error_message = self._parse_json(
  85. e.cause.read().decode(), video_id)['message']
  86. raise ExtractorError('%s said: %s' % (
  87. self.IE_NAME, error_message), expected=True)
  88. raise
  89. video = info['video_product']
  90. title = info['title'].strip()
  91. formats = self._extract_m3u8_formats(
  92. video['url'], video_id, 'mp4', entry_protocol='m3u8_native',
  93. m3u8_id='hls')
  94. self._sort_formats(formats)
  95. subtitles = {}
  96. for _, captions in (try_get(
  97. video, lambda x: x['attachments']['captions'],
  98. dict) or {}).items():
  99. if not captions or not isinstance(captions, list):
  100. continue
  101. for caption in captions:
  102. caption_url = caption.get('url')
  103. if not caption_url:
  104. continue
  105. ext = caption.get('format')
  106. if ext == 'xml':
  107. ext = 'ttml'
  108. subtitles.setdefault(caption.get('lang') or 'en', []).append({
  109. 'url': caption_url,
  110. 'ext': ext,
  111. })
  112. subheading = info.get('subheading')
  113. if subheading:
  114. title += ' - %s' % subheading
  115. return {
  116. 'id': video_id,
  117. 'title': title,
  118. 'description': info.get('long_description') or info.get(
  119. 'short_description'),
  120. 'duration': float_or_none(video.get('duration'), scale=1000),
  121. # 'timestamp': unified_timestamp(info.get('published')),
  122. 'series': info.get('show_title'),
  123. 'season_number': int_or_none(info.get('season_number')),
  124. 'episode_number': int_or_none(info.get('episode_number')),
  125. 'formats': formats,
  126. 'subtitles': subtitles,
  127. }