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.

159 lines
5.7 KiB

  1. # coding: utf-8
  2. from __future__ import unicode_literals
  3. import re
  4. from .common import InfoExtractor
  5. from ..utils import (
  6. int_or_none,
  7. urlencode_postdata,
  8. compat_str,
  9. ExtractorError,
  10. )
  11. class CuriosityStreamBaseIE(InfoExtractor):
  12. _NETRC_MACHINE = 'curiositystream'
  13. _auth_token = None
  14. _API_BASE_URL = 'https://api.curiositystream.com/v1/'
  15. def _handle_errors(self, result):
  16. error = result.get('error', {}).get('message')
  17. if error:
  18. if isinstance(error, dict):
  19. error = ', '.join(error.values())
  20. raise ExtractorError(
  21. '%s said: %s' % (self.IE_NAME, error), expected=True)
  22. def _call_api(self, path, video_id):
  23. headers = {}
  24. if self._auth_token:
  25. headers['X-Auth-Token'] = self._auth_token
  26. result = self._download_json(
  27. self._API_BASE_URL + path, video_id, headers=headers)
  28. self._handle_errors(result)
  29. return result['data']
  30. def _real_initialize(self):
  31. (email, password) = self._get_login_info()
  32. if email is None:
  33. return
  34. result = self._download_json(
  35. self._API_BASE_URL + 'login', None, data=urlencode_postdata({
  36. 'email': email,
  37. 'password': password,
  38. }))
  39. self._handle_errors(result)
  40. self._auth_token = result['message']['auth_token']
  41. def _extract_media_info(self, media):
  42. video_id = compat_str(media['id'])
  43. title = media['title']
  44. formats = []
  45. for encoding in media.get('encodings', []):
  46. m3u8_url = encoding.get('master_playlist_url')
  47. if m3u8_url:
  48. formats.extend(self._extract_m3u8_formats(
  49. m3u8_url, video_id, 'mp4', 'm3u8_native',
  50. m3u8_id='hls', fatal=False))
  51. encoding_url = encoding.get('url')
  52. file_url = encoding.get('file_url')
  53. if not encoding_url and not file_url:
  54. continue
  55. f = {
  56. 'width': int_or_none(encoding.get('width')),
  57. 'height': int_or_none(encoding.get('height')),
  58. 'vbr': int_or_none(encoding.get('video_bitrate')),
  59. 'abr': int_or_none(encoding.get('audio_bitrate')),
  60. 'filesize': int_or_none(encoding.get('size_in_bytes')),
  61. 'vcodec': encoding.get('video_codec'),
  62. 'acodec': encoding.get('audio_codec'),
  63. 'container': encoding.get('container_type'),
  64. }
  65. for f_url in (encoding_url, file_url):
  66. if not f_url:
  67. continue
  68. fmt = f.copy()
  69. rtmp = re.search(r'^(?P<url>rtmpe?://(?P<host>[^/]+)/(?P<app>.+))/(?P<playpath>mp[34]:.+)$', f_url)
  70. if rtmp:
  71. fmt.update({
  72. 'url': rtmp.group('url'),
  73. 'play_path': rtmp.group('playpath'),
  74. 'app': rtmp.group('app'),
  75. 'ext': 'flv',
  76. 'format_id': 'rtmp',
  77. })
  78. else:
  79. fmt.update({
  80. 'url': f_url,
  81. 'format_id': 'http',
  82. })
  83. formats.append(fmt)
  84. self._sort_formats(formats)
  85. subtitles = {}
  86. for closed_caption in media.get('closed_captions', []):
  87. sub_url = closed_caption.get('file')
  88. if not sub_url:
  89. continue
  90. lang = closed_caption.get('code') or closed_caption.get('language') or 'en'
  91. subtitles.setdefault(lang, []).append({
  92. 'url': sub_url,
  93. })
  94. return {
  95. 'id': video_id,
  96. 'formats': formats,
  97. 'title': title,
  98. 'description': media.get('description'),
  99. 'thumbnail': media.get('image_large') or media.get('image_medium') or media.get('image_small'),
  100. 'duration': int_or_none(media.get('duration')),
  101. 'tags': media.get('tags'),
  102. 'subtitles': subtitles,
  103. }
  104. class CuriosityStreamIE(CuriosityStreamBaseIE):
  105. IE_NAME = 'curiositystream'
  106. _VALID_URL = r'https?://app\.curiositystream\.com/video/(?P<id>\d+)'
  107. _TEST = {
  108. 'url': 'https://app.curiositystream.com/video/2',
  109. 'md5': '262bb2f257ff301115f1973540de8983',
  110. 'info_dict': {
  111. 'id': '2',
  112. 'ext': 'mp4',
  113. 'title': 'How Did You Develop The Internet?',
  114. 'description': 'Vint Cerf, Google\'s Chief Internet Evangelist, describes how he and Bob Kahn created the internet.',
  115. }
  116. }
  117. def _real_extract(self, url):
  118. video_id = self._match_id(url)
  119. media = self._call_api('media/' + video_id, video_id)
  120. return self._extract_media_info(media)
  121. class CuriosityStreamCollectionIE(CuriosityStreamBaseIE):
  122. IE_NAME = 'curiositystream:collection'
  123. _VALID_URL = r'https?://app\.curiositystream\.com/collection/(?P<id>\d+)'
  124. _TEST = {
  125. 'url': 'https://app.curiositystream.com/collection/2',
  126. 'info_dict': {
  127. 'id': '2',
  128. 'title': 'Curious Minds: The Internet',
  129. 'description': 'How is the internet shaping our lives in the 21st Century?',
  130. },
  131. 'playlist_mincount': 12,
  132. }
  133. def _real_extract(self, url):
  134. collection_id = self._match_id(url)
  135. collection = self._call_api(
  136. 'collections/' + collection_id, collection_id)
  137. entries = []
  138. for media in collection.get('media', []):
  139. entries.append(self._extract_media_info(media))
  140. return self.playlist_result(
  141. entries, collection_id,
  142. collection.get('title'), collection.get('description'))