zype.py 5.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135
  1. import re
  2. from .common import InfoExtractor
  3. from ..networking.exceptions import HTTPError
  4. from ..utils import (
  5. ExtractorError,
  6. dict_get,
  7. int_or_none,
  8. js_to_json,
  9. parse_iso8601,
  10. )
  11. class ZypeIE(InfoExtractor):
  12. _ID_RE = r'[\da-fA-F]+'
  13. _COMMON_RE = r'//player\.zype\.com/embed/%s\.(?:js|json|html)\?.*?(?:access_token|(?:ap[ip]|player)_key)='
  14. _VALID_URL = r'https?:%s[^&]+' % (_COMMON_RE % (f'(?P<id>{_ID_RE})'))
  15. _EMBED_REGEX = [fr'<script[^>]+\bsrc=(["\'])(?P<url>(?:https?:)?{_COMMON_RE % _ID_RE}.+?)\1']
  16. _TEST = {
  17. 'url': 'https://player.zype.com/embed/5b400b834b32992a310622b9.js?api_key=jZ9GUhRmxcPvX7M3SlfejB6Hle9jyHTdk2jVxG7wOHPLODgncEKVdPYBhuz9iWXQ&autoplay=false&controls=true&da=false',
  18. 'md5': 'eaee31d474c76a955bdaba02a505c595',
  19. 'info_dict': {
  20. 'id': '5b400b834b32992a310622b9',
  21. 'ext': 'mp4',
  22. 'title': 'Smoky Barbecue Favorites',
  23. 'thumbnail': r're:^https?://.*\.jpe?g',
  24. 'description': 'md5:5ff01e76316bd8d46508af26dc86023b',
  25. 'timestamp': 1504915200,
  26. 'upload_date': '20170909',
  27. },
  28. }
  29. def _real_extract(self, url):
  30. video_id = self._match_id(url)
  31. try:
  32. response = self._download_json(re.sub(
  33. r'\.(?:js|html)\?', '.json?', url), video_id)['response']
  34. except ExtractorError as e:
  35. if isinstance(e.cause, HTTPError) and e.cause.status in (400, 401, 403):
  36. raise ExtractorError(self._parse_json(
  37. e.cause.response.read().decode(), video_id)['message'], expected=True)
  38. raise
  39. body = response['body']
  40. video = response['video']
  41. title = video['title']
  42. subtitles = {}
  43. if isinstance(body, dict):
  44. formats = []
  45. for output in body.get('outputs', []):
  46. output_url = output.get('url')
  47. if not output_url:
  48. continue
  49. name = output.get('name')
  50. if name == 'm3u8':
  51. formats, subtitles = self._extract_m3u8_formats_and_subtitles(
  52. output_url, video_id, 'mp4',
  53. 'm3u8_native', m3u8_id='hls', fatal=False)
  54. else:
  55. f = {
  56. 'format_id': name,
  57. 'tbr': int_or_none(output.get('bitrate')),
  58. 'url': output_url,
  59. }
  60. if name in ('m4a', 'mp3'):
  61. f['vcodec'] = 'none'
  62. else:
  63. f.update({
  64. 'height': int_or_none(output.get('height')),
  65. 'width': int_or_none(output.get('width')),
  66. })
  67. formats.append(f)
  68. text_tracks = body.get('subtitles') or []
  69. else:
  70. m3u8_url = self._search_regex(
  71. r'(["\'])(?P<url>(?:(?!\1).)+\.m3u8(?:(?!\1).)*)\1',
  72. body, 'm3u8 url', group='url', default=None)
  73. if not m3u8_url:
  74. source = self._search_regex(
  75. r'(?s)sources\s*:\s*\[\s*({.+?})\s*\]', body, 'source')
  76. def get_attr(key):
  77. return self._search_regex(
  78. rf'\b{key}\s*:\s*([\'"])(?P<val>(?:(?!\1).)+)\1',
  79. source, key, group='val')
  80. if get_attr('integration') == 'verizon-media':
  81. m3u8_url = 'https://content.uplynk.com/{}.m3u8'.format(get_attr('id'))
  82. formats, subtitles = self._extract_m3u8_formats_and_subtitles(
  83. m3u8_url, video_id, 'mp4', 'm3u8_native', m3u8_id='hls')
  84. text_tracks = self._search_regex(
  85. r'textTracks\s*:\s*(\[[^]]+\])',
  86. body, 'text tracks', default=None)
  87. if text_tracks:
  88. text_tracks = self._parse_json(
  89. text_tracks, video_id, js_to_json, False)
  90. if text_tracks:
  91. for text_track in text_tracks:
  92. tt_url = dict_get(text_track, ('file', 'src'))
  93. if not tt_url:
  94. continue
  95. subtitles.setdefault(text_track.get('label') or 'English', []).append({
  96. 'url': tt_url,
  97. })
  98. thumbnails = []
  99. for thumbnail in video.get('thumbnails', []):
  100. thumbnail_url = thumbnail.get('url')
  101. if not thumbnail_url:
  102. continue
  103. thumbnails.append({
  104. 'url': thumbnail_url,
  105. 'width': int_or_none(thumbnail.get('width')),
  106. 'height': int_or_none(thumbnail.get('height')),
  107. })
  108. return {
  109. 'id': video_id,
  110. 'display_id': video.get('friendly_title'),
  111. 'title': title,
  112. 'thumbnails': thumbnails,
  113. 'description': dict_get(video, ('description', 'ott_description', 'short_description')),
  114. 'timestamp': parse_iso8601(video.get('published_at')),
  115. 'duration': int_or_none(video.get('duration')),
  116. 'view_count': int_or_none(video.get('request_count')),
  117. 'average_rating': int_or_none(video.get('rating')),
  118. 'season_number': int_or_none(video.get('season')),
  119. 'episode_number': int_or_none(video.get('episode')),
  120. 'formats': formats,
  121. 'subtitles': subtitles,
  122. }