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.

151 lines
5.3 KiB

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