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