whowatch.py 3.8 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697
  1. from .common import InfoExtractor
  2. from ..utils import (
  3. int_or_none,
  4. qualities,
  5. try_call,
  6. try_get,
  7. ExtractorError,
  8. )
  9. from ..compat import compat_str
  10. class WhoWatchIE(InfoExtractor):
  11. IE_NAME = 'whowatch'
  12. _VALID_URL = r'https?://whowatch\.tv/viewer/(?P<id>\d+)'
  13. _TESTS = [{
  14. 'url': 'https://whowatch.tv/viewer/21450171',
  15. 'only_matching': True,
  16. }]
  17. def _real_extract(self, url):
  18. video_id = self._match_id(url)
  19. self._download_webpage(url, video_id)
  20. metadata = self._download_json('https://api.whowatch.tv/lives/%s' % video_id, video_id)
  21. live_data = self._download_json('https://api.whowatch.tv/lives/%s/play' % video_id, video_id)
  22. title = try_call(
  23. lambda: live_data['share_info']['live_title'][1:-1],
  24. lambda: metadata['live']['title'],
  25. expected_type=str)
  26. hls_url = live_data.get('hls_url')
  27. if not hls_url:
  28. raise ExtractorError(live_data.get('error_message') or 'The user is offline.', expected=True)
  29. QUALITIES = qualities(['low', 'medium', 'high', 'veryhigh'])
  30. formats = []
  31. for i, fmt in enumerate(live_data.get('streams') or []):
  32. name = fmt.get('quality') or fmt.get('name') or compat_str(i)
  33. hls_url = fmt.get('hls_url')
  34. rtmp_url = fmt.get('rtmp_url')
  35. audio_only = fmt.get('audio_only')
  36. quality = QUALITIES(fmt.get('quality'))
  37. if hls_url:
  38. hls_fmts = self._extract_m3u8_formats(
  39. hls_url, video_id, ext='mp4', m3u8_id='hls-%s' % name, quality=quality)
  40. formats.extend(hls_fmts)
  41. else:
  42. hls_fmts = []
  43. # RTMP url for audio_only is same as high format, so skip it
  44. if rtmp_url and not audio_only:
  45. formats.append({
  46. 'url': rtmp_url,
  47. 'format_id': 'rtmp-%s' % name,
  48. 'ext': 'mp4',
  49. 'protocol': 'rtmp_ffmpeg', # ffmpeg can, while rtmpdump can't
  50. 'vcodec': 'h264',
  51. 'acodec': 'aac',
  52. 'quality': quality,
  53. 'format_note': fmt.get('label'),
  54. # note: HLS and RTMP have same resolution for now, so it's acceptable
  55. 'width': try_get(hls_fmts, lambda x: x[0]['width'], int),
  56. 'height': try_get(hls_fmts, lambda x: x[0]['height'], int),
  57. })
  58. # This contains the same formats as the above manifests and is used only as a fallback
  59. formats.extend(self._extract_m3u8_formats(
  60. hls_url, video_id, ext='mp4', m3u8_id='hls'))
  61. self._remove_duplicate_formats(formats)
  62. uploader_url = try_get(metadata, lambda x: x['live']['user']['user_path'], compat_str)
  63. if uploader_url:
  64. uploader_url = 'https://whowatch.tv/profile/%s' % uploader_url
  65. uploader_id = compat_str(try_get(metadata, lambda x: x['live']['user']['id'], int))
  66. uploader = try_get(metadata, lambda x: x['live']['user']['name'], compat_str)
  67. thumbnail = try_get(metadata, lambda x: x['live']['latest_thumbnail_url'], compat_str)
  68. timestamp = int_or_none(try_get(metadata, lambda x: x['live']['started_at'], int), scale=1000)
  69. view_count = try_get(metadata, lambda x: x['live']['total_view_count'], int)
  70. comment_count = try_get(metadata, lambda x: x['live']['comment_count'], int)
  71. return {
  72. 'id': video_id,
  73. 'title': title,
  74. 'uploader_id': uploader_id,
  75. 'uploader_url': uploader_url,
  76. 'uploader': uploader,
  77. 'formats': formats,
  78. 'thumbnail': thumbnail,
  79. 'timestamp': timestamp,
  80. 'view_count': view_count,
  81. 'comment_count': comment_count,
  82. 'is_live': True,
  83. }