mirrativ.py 4.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118
  1. from .common import InfoExtractor
  2. from ..utils import (
  3. ExtractorError,
  4. dict_get,
  5. traverse_obj,
  6. try_get,
  7. )
  8. class MirrativBaseIE(InfoExtractor):
  9. def assert_error(self, response):
  10. error_message = traverse_obj(response, ('status', 'error'))
  11. if error_message:
  12. raise ExtractorError(f'Mirrativ says: {error_message}', expected=True)
  13. class MirrativIE(MirrativBaseIE):
  14. IE_NAME = 'mirrativ'
  15. _VALID_URL = r'https?://(?:www\.)?mirrativ\.com/live/(?P<id>[^/?#&]+)'
  16. TESTS = [{
  17. 'url': 'https://mirrativ.com/live/UQomuS7EMgHoxRHjEhNiHw',
  18. 'info_dict': {
  19. 'id': 'UQomuS7EMgHoxRHjEhNiHw',
  20. 'title': 'ねむいぃ、。『参加型』🔰jcが初めてやるCOD✨初見さん大歓迎💗',
  21. 'is_live': True,
  22. 'description': 'md5:bfcd8f77f2fab24c3c672e5620f3f16e',
  23. 'thumbnail': r're:https?://.+',
  24. 'uploader': '# あ ち ゅ 。💡',
  25. 'uploader_id': '118572165',
  26. 'duration': None,
  27. 'view_count': 1241,
  28. 'release_timestamp': 1646229192,
  29. 'timestamp': 1646229167,
  30. 'was_live': False,
  31. },
  32. 'skip': 'livestream',
  33. }, {
  34. 'url': 'https://mirrativ.com/live/POxyuG1KmW2982lqlDTuPw',
  35. 'only_matching': True,
  36. }]
  37. def _real_extract(self, url):
  38. video_id = self._match_id(url)
  39. webpage = self._download_webpage(f'https://www.mirrativ.com/live/{video_id}', video_id)
  40. live_response = self._download_json(f'https://www.mirrativ.com/api/live/live?live_id={video_id}', video_id)
  41. self.assert_error(live_response)
  42. hls_url = dict_get(live_response, ('archive_url_hls', 'streaming_url_hls'))
  43. is_live = bool(live_response.get('is_live'))
  44. if not hls_url:
  45. raise ExtractorError('Neither archive nor live is available.', expected=True)
  46. formats = self._extract_m3u8_formats(
  47. hls_url, video_id,
  48. ext='mp4', entry_protocol='m3u8_native',
  49. m3u8_id='hls', live=is_live)
  50. return {
  51. 'id': video_id,
  52. 'title': self._og_search_title(webpage, default=None) or self._search_regex(
  53. r'<title>\s*(.+?) - Mirrativ\s*</title>', webpage) or live_response.get('title'),
  54. 'is_live': is_live,
  55. 'description': live_response.get('description'),
  56. 'formats': formats,
  57. 'thumbnail': live_response.get('image_url'),
  58. 'uploader': traverse_obj(live_response, ('owner', 'name')),
  59. 'uploader_id': traverse_obj(live_response, ('owner', 'user_id')),
  60. 'duration': try_get(live_response, lambda x: x['ended_at'] - x['started_at']) if not is_live else None,
  61. 'view_count': live_response.get('total_viewer_num'),
  62. 'release_timestamp': live_response.get('started_at'),
  63. 'timestamp': live_response.get('created_at'),
  64. 'was_live': bool(live_response.get('is_archive')),
  65. }
  66. class MirrativUserIE(MirrativBaseIE):
  67. IE_NAME = 'mirrativ:user'
  68. _VALID_URL = r'https?://(?:www\.)?mirrativ\.com/user/(?P<id>\d+)'
  69. _TESTS = [{
  70. # Live archive is available up to 3 days
  71. # see: https://helpfeel.com/mirrativ/%E9%8C%B2%E7%94%BB-5e26d3ad7b59ef0017fb49ac (Japanese)
  72. 'url': 'https://www.mirrativ.com/user/110943130',
  73. 'note': 'multiple archives available',
  74. 'only_matching': True,
  75. }]
  76. def _entries(self, user_id):
  77. page = 1
  78. while page is not None:
  79. api_response = self._download_json(
  80. f'https://www.mirrativ.com/api/live/live_history?user_id={user_id}&page={page}', user_id,
  81. note=f'Downloading page {page}')
  82. self.assert_error(api_response)
  83. lives = api_response.get('lives')
  84. if not lives:
  85. break
  86. for live in lives:
  87. if not live.get('is_archive') and not live.get('is_live'):
  88. # neither archive nor live is available, so skip it
  89. # or the service will ban your IP address for a while
  90. continue
  91. live_id = live.get('live_id')
  92. url = f'https://www.mirrativ.com/live/{live_id}'
  93. yield self.url_result(url, video_id=live_id, video_title=live.get('title'))
  94. page = api_response.get('next_page')
  95. def _real_extract(self, url):
  96. user_id = self._match_id(url)
  97. user_info = self._download_json(
  98. f'https://www.mirrativ.com/api/user/profile?user_id={user_id}', user_id,
  99. note='Downloading user info', fatal=False)
  100. self.assert_error(user_info)
  101. return self.playlist_result(
  102. self._entries(user_id), user_id,
  103. user_info.get('name'), user_info.get('description'))