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.

142 lines
5.1 KiB

  1. from __future__ import unicode_literals
  2. from .common import InfoExtractor
  3. from ..compat import (
  4. compat_urllib_parse,
  5. compat_parse_qs,
  6. compat_urllib_parse_urlparse,
  7. compat_urlparse,
  8. )
  9. from ..utils import (
  10. ExtractorError,
  11. parse_duration,
  12. replace_extension,
  13. )
  14. class FiveMinIE(InfoExtractor):
  15. IE_NAME = '5min'
  16. _VALID_URL = r'''(?x)
  17. (?:https?://[^/]*?5min\.com/Scripts/PlayerSeed\.js\?(?:.*?&)?playList=|
  18. https?://(?:(?:massively|www)\.)?joystiq\.com/video/|
  19. 5min:)
  20. (?P<id>\d+)
  21. '''
  22. _TESTS = [
  23. {
  24. # From http://www.engadget.com/2013/11/15/ipad-mini-retina-display-review/
  25. 'url': 'http://pshared.5min.com/Scripts/PlayerSeed.js?sid=281&width=560&height=345&playList=518013791',
  26. 'md5': '4f7b0b79bf1a470e5004f7112385941d',
  27. 'info_dict': {
  28. 'id': '518013791',
  29. 'ext': 'mp4',
  30. 'title': 'iPad Mini with Retina Display Review',
  31. 'duration': 177,
  32. },
  33. },
  34. {
  35. # From http://on.aol.com/video/how-to-make-a-next-level-fruit-salad-518086247
  36. 'url': '5min:518086247',
  37. 'md5': 'e539a9dd682c288ef5a498898009f69e',
  38. 'info_dict': {
  39. 'id': '518086247',
  40. 'ext': 'mp4',
  41. 'title': 'How to Make a Next-Level Fruit Salad',
  42. 'duration': 184,
  43. },
  44. },
  45. ]
  46. _ERRORS = {
  47. 'ErrorVideoNotExist': 'We\'re sorry, but the video you are trying to watch does not exist.',
  48. 'ErrorVideoNoLongerAvailable': 'We\'re sorry, but the video you are trying to watch is no longer available.',
  49. 'ErrorVideoRejected': 'We\'re sorry, but the video you are trying to watch has been removed.',
  50. 'ErrorVideoUserNotGeo': 'We\'re sorry, but the video you are trying to watch cannot be viewed from your current location.',
  51. 'ErrorVideoLibraryRestriction': 'We\'re sorry, but the video you are trying to watch is currently unavailable for viewing at this domain.',
  52. 'ErrorExposurePermission': 'We\'re sorry, but the video you are trying to watch is currently unavailable for viewing at this domain.',
  53. }
  54. _QUALITIES = {
  55. 1: {
  56. 'width': 640,
  57. 'height': 360,
  58. },
  59. 2: {
  60. 'width': 854,
  61. 'height': 480,
  62. },
  63. 4: {
  64. 'width': 1280,
  65. 'height': 720,
  66. },
  67. 8: {
  68. 'width': 1920,
  69. 'height': 1080,
  70. },
  71. 16: {
  72. 'width': 640,
  73. 'height': 360,
  74. },
  75. 32: {
  76. 'width': 854,
  77. 'height': 480,
  78. },
  79. 64: {
  80. 'width': 1280,
  81. 'height': 720,
  82. },
  83. 128: {
  84. 'width': 640,
  85. 'height': 360,
  86. },
  87. }
  88. def _real_extract(self, url):
  89. video_id = self._match_id(url)
  90. embed_url = 'https://embed.5min.com/playerseed/?playList=%s' % video_id
  91. embed_page = self._download_webpage(embed_url, video_id,
  92. 'Downloading embed page')
  93. sid = self._search_regex(r'sid=(\d+)', embed_page, 'sid')
  94. query = compat_urllib_parse.urlencode({
  95. 'func': 'GetResults',
  96. 'playlist': video_id,
  97. 'sid': sid,
  98. 'isPlayerSeed': 'true',
  99. 'url': embed_url,
  100. })
  101. response = self._download_json(
  102. 'https://syn.5min.com/handlers/SenseHandler.ashx?' + query,
  103. video_id)
  104. if not response['success']:
  105. raise ExtractorError(
  106. '%s said: %s' % (
  107. self.IE_NAME,
  108. self._ERRORS.get(response['errorMessage'], response['errorMessage'])),
  109. expected=True)
  110. info = response['binding'][0]
  111. formats = []
  112. parsed_video_url = compat_urllib_parse_urlparse(compat_parse_qs(
  113. compat_urllib_parse_urlparse(info['EmbededURL']).query)['videoUrl'][0])
  114. for rendition in info['Renditions']:
  115. if rendition['RenditionType'] == 'm3u8':
  116. formats.extend(self._extract_m3u8_formats(rendition['Url'], video_id, m3u8_id='hls'))
  117. elif rendition['RenditionType'] == 'aac':
  118. continue
  119. else:
  120. rendition_url = compat_urlparse.urlunparse(parsed_video_url._replace(path=replace_extension(parsed_video_url.path.replace('//', '/%s/' % rendition['ID']), rendition['RenditionType'])))
  121. quality = self._QUALITIES.get(rendition['ID'], {})
  122. formats.append({
  123. 'format_id': '%s-%d' % (rendition['RenditionType'], rendition['ID']),
  124. 'url': rendition_url,
  125. 'width': quality.get('width'),
  126. 'height': quality.get('height'),
  127. })
  128. self._sort_formats(formats)
  129. return {
  130. 'id': video_id,
  131. 'title': info['Title'],
  132. 'thumbnail': info.get('ThumbURL'),
  133. 'duration': parse_duration(info.get('Duration')),
  134. 'formats': formats,
  135. }