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.

158 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. ExtractorError,
  7. int_or_none,
  8. str_or_none,
  9. )
  10. class VVVVIDIE(InfoExtractor):
  11. _VALID_URL = r'https?://(?:www\.)?vvvvid\.it/#!(?:show|anime|film|series)/(?P<show_id>\d+)/[^/]+/(?P<season_id>\d+)/(?P<id>[0-9]+)'
  12. _TESTS = [{
  13. # video_type == 'video/vvvvid'
  14. 'url': 'https://www.vvvvid.it/#!show/434/perche-dovrei-guardarlo-di-dario-moccia/437/489048/ping-pong',
  15. 'md5': 'b8d3cecc2e981adc3835adf07f6df91b',
  16. 'info_dict': {
  17. 'id': '489048',
  18. 'ext': 'mp4',
  19. 'title': 'Ping Pong',
  20. },
  21. 'params': {
  22. 'skip_download': True,
  23. },
  24. }, {
  25. # video_type == 'video/rcs'
  26. 'url': 'https://www.vvvvid.it/#!show/376/death-note-live-action/377/482493/episodio-01',
  27. 'md5': '33e0edfba720ad73a8782157fdebc648',
  28. 'info_dict': {
  29. 'id': '482493',
  30. 'ext': 'mp4',
  31. 'title': 'Episodio 01',
  32. },
  33. 'params': {
  34. 'skip_download': True,
  35. },
  36. }]
  37. _conn_id = None
  38. def _real_initialize(self):
  39. self._conn_id = self._download_json(
  40. 'https://www.vvvvid.it/user/login',
  41. None, headers=self.geo_verification_headers())['data']['conn_id']
  42. def _real_extract(self, url):
  43. show_id, season_id, video_id = re.match(self._VALID_URL, url).groups()
  44. response = self._download_json(
  45. 'https://www.vvvvid.it/vvvvid/ondemand/%s/season/%s' % (show_id, season_id),
  46. video_id, headers=self.geo_verification_headers(), query={
  47. 'conn_id': self._conn_id,
  48. })
  49. if response['result'] == 'error':
  50. raise ExtractorError('%s said: %s' % (
  51. self.IE_NAME, response['message']), expected=True)
  52. vid = int(video_id)
  53. video_data = list(filter(
  54. lambda episode: episode.get('video_id') == vid, response['data']))[0]
  55. formats = []
  56. # vvvvid embed_info decryption algorithm is reverse engineered from function $ds(h) at vvvvid.js
  57. def ds(h):
  58. g = "MNOPIJKL89+/4567UVWXQRSTEFGHABCDcdefYZabstuvopqr0123wxyzklmnghij"
  59. def f(m):
  60. l = []
  61. o = 0
  62. b = False
  63. m_len = len(m)
  64. while ((not b) and o < m_len):
  65. n = m[o] << 2
  66. o += 1
  67. k = -1
  68. j = -1
  69. if o < m_len:
  70. n += m[o] >> 4
  71. o += 1
  72. if o < m_len:
  73. k = (m[o - 1] << 4) & 255
  74. k += m[o] >> 2
  75. o += 1
  76. if o < m_len:
  77. j = (m[o - 1] << 6) & 255
  78. j += m[o]
  79. o += 1
  80. else:
  81. b = True
  82. else:
  83. b = True
  84. else:
  85. b = True
  86. l.append(n)
  87. if k != -1:
  88. l.append(k)
  89. if j != -1:
  90. l.append(j)
  91. return l
  92. c = []
  93. for e in h:
  94. c.append(g.index(e))
  95. c_len = len(c)
  96. for e in range(c_len * 2 - 1, -1, -1):
  97. a = c[e % c_len] ^ c[(e + 1) % c_len]
  98. c[e % c_len] = a
  99. c = f(c)
  100. d = ''
  101. for e in c:
  102. d += chr(e)
  103. return d
  104. for quality in ('_sd', ''):
  105. embed_code = video_data.get('embed_info' + quality)
  106. if not embed_code:
  107. continue
  108. embed_code = ds(embed_code)
  109. video_type = video_data.get('video_type')
  110. if video_type in ('video/rcs', 'video/kenc'):
  111. embed_code = re.sub(r'https?://([^/]+)/z/', r'https://\1/i/', embed_code).replace('/manifest.f4m', '/master.m3u8')
  112. if video_type == 'video/kenc':
  113. kenc = self._download_json(
  114. 'https://www.vvvvid.it/kenc', video_id, query={
  115. 'action': 'kt',
  116. 'conn_id': self._conn_id,
  117. 'url': embed_code,
  118. }, fatal=False) or {}
  119. kenc_message = kenc.get('message')
  120. if kenc_message:
  121. embed_code += '?' + ds(kenc_message)
  122. formats.extend(self._extract_m3u8_formats(
  123. embed_code, video_id, 'mp4',
  124. m3u8_id='hls', fatal=False))
  125. else:
  126. formats.extend(self._extract_wowza_formats(
  127. 'http://sb.top-ix.org/videomg/_definst_/mp4:%s/playlist.m3u8' % embed_code, video_id))
  128. self._sort_formats(formats)
  129. return {
  130. 'id': video_id,
  131. 'title': video_data['title'],
  132. 'formats': formats,
  133. 'thumbnail': video_data.get('thumbnail'),
  134. 'duration': int_or_none(video_data.get('length')),
  135. 'series': video_data.get('show_title'),
  136. 'season_id': season_id,
  137. 'season_number': video_data.get('season_number'),
  138. 'episode_id': str_or_none(video_data.get('id')),
  139. 'episode_number': int_or_none(video_data.get('number')),
  140. 'episode_title': video_data['title'],
  141. 'view_count': int_or_none(video_data.get('views')),
  142. 'like_count': int_or_none(video_data.get('video_likes')),
  143. }