]>
Commit | Line | Data |
---|---|---|
ff5e16f2 THD |
1 | from __future__ import unicode_literals |
2 | ||
3 | from .common import InfoExtractor | |
4 | from ..utils import ( | |
5 | ExtractorError, | |
6 | dict_get, | |
7 | traverse_obj, | |
8 | try_get, | |
9 | ) | |
10 | ||
11 | ||
12 | class MirrativBaseIE(InfoExtractor): | |
13 | def assert_error(self, response): | |
14 | error_message = traverse_obj(response, ('status', 'error')) | |
15 | if error_message: | |
16 | raise ExtractorError('Mirrativ says: %s' % error_message, expected=True) | |
17 | ||
18 | ||
19 | class MirrativIE(MirrativBaseIE): | |
20 | IE_NAME = 'mirrativ' | |
21 | _VALID_URL = r'https?://(?:www\.)?mirrativ\.com/live/(?P<id>[^/?#&]+)' | |
22 | LIVE_API_URL = 'https://www.mirrativ.com/api/live/live?live_id=%s' | |
23 | ||
24 | TESTS = [{ | |
25 | 'url': 'https://mirrativ.com/live/POxyuG1KmW2982lqlDTuPw', | |
26 | 'only_matching': True, | |
27 | }] | |
28 | ||
29 | def _real_extract(self, url): | |
30 | video_id = self._match_id(url) | |
31 | webpage = self._download_webpage('https://www.mirrativ.com/live/%s' % video_id, video_id) | |
32 | live_response = self._download_json(self.LIVE_API_URL % video_id, video_id) | |
33 | self.assert_error(live_response) | |
34 | ||
35 | hls_url = dict_get(live_response, ('archive_url_hls', 'streaming_url_hls')) | |
36 | is_live = bool(live_response.get('is_live')) | |
37 | was_live = bool(live_response.get('is_archive')) | |
38 | if not hls_url: | |
39 | raise ExtractorError('Neither archive nor live is available.', expected=True) | |
40 | ||
41 | formats = self._extract_m3u8_formats( | |
42 | hls_url, video_id, | |
43 | ext='mp4', entry_protocol='m3u8_native', | |
44 | m3u8_id='hls', live=is_live) | |
45 | rtmp_url = live_response.get('streaming_url_edge') | |
46 | if rtmp_url: | |
47 | keys_to_copy = ('width', 'height', 'vcodec', 'acodec', 'tbr') | |
48 | fmt = { | |
49 | 'format_id': 'rtmp', | |
50 | 'url': rtmp_url, | |
51 | 'protocol': 'rtmp', | |
52 | 'ext': 'mp4', | |
53 | } | |
54 | fmt.update({k: traverse_obj(formats, (0, k)) for k in keys_to_copy}) | |
55 | formats.append(fmt) | |
56 | self._sort_formats(formats) | |
57 | ||
58 | title = self._og_search_title(webpage, default=None) or self._search_regex( | |
59 | r'<title>\s*(.+?) - Mirrativ\s*</title>', webpage) or live_response.get('title') | |
60 | description = live_response.get('description') | |
61 | thumbnail = live_response.get('image_url') | |
62 | ||
63 | duration = try_get(live_response, lambda x: x['ended_at'] - x['started_at']) | |
64 | view_count = live_response.get('total_viewer_num') | |
65 | release_timestamp = live_response.get('started_at') | |
66 | timestamp = live_response.get('created_at') | |
67 | ||
68 | owner = live_response.get('owner', {}) | |
69 | uploader = owner.get('name') | |
70 | uploader_id = owner.get('user_id') | |
71 | ||
72 | return { | |
73 | 'id': video_id, | |
74 | 'title': title, | |
75 | 'is_live': is_live, | |
76 | 'description': description, | |
77 | 'formats': formats, | |
78 | 'thumbnail': thumbnail, | |
79 | 'uploader': uploader, | |
80 | 'uploader_id': uploader_id, | |
81 | 'duration': duration, | |
82 | 'view_count': view_count, | |
83 | 'release_timestamp': release_timestamp, | |
84 | 'timestamp': timestamp, | |
85 | 'was_live': was_live, | |
86 | } | |
87 | ||
88 | ||
89 | class MirrativUserIE(MirrativBaseIE): | |
90 | IE_NAME = 'mirrativ:user' | |
91 | _VALID_URL = r'https?://(?:www\.)?mirrativ\.com/user/(?P<id>\d+)' | |
92 | LIVE_HISTORY_API_URL = 'https://www.mirrativ.com/api/live/live_history?user_id=%s&page=%d' | |
93 | USER_INFO_API_URL = 'https://www.mirrativ.com/api/user/profile?user_id=%s' | |
94 | ||
95 | _TESTS = [{ | |
96 | # Live archive is available up to 3 days | |
97 | # see: https://helpfeel.com/mirrativ/%E9%8C%B2%E7%94%BB-5e26d3ad7b59ef0017fb49ac (Japanese) | |
98 | 'url': 'https://www.mirrativ.com/user/110943130', | |
99 | 'note': 'multiple archives available', | |
100 | 'only_matching': True, | |
101 | }] | |
102 | ||
103 | def _entries(self, user_id): | |
104 | page = 1 | |
105 | while page is not None: | |
106 | api_response = self._download_json( | |
107 | self.LIVE_HISTORY_API_URL % (user_id, page), user_id, | |
108 | note='Downloading page %d' % page) | |
109 | self.assert_error(api_response) | |
110 | lives = api_response.get('lives') | |
111 | if not lives: | |
112 | break | |
113 | for live in lives: | |
114 | if not live.get('is_archive') and not live.get('is_live'): | |
115 | # neither archive nor live is available, so skip it | |
116 | # or the service will ban your IP address for a while | |
117 | continue | |
118 | live_id = live.get('live_id') | |
119 | url = 'https://www.mirrativ.com/live/%s' % live_id | |
120 | yield self.url_result(url, video_id=live_id, video_title=live.get('title')) | |
121 | page = api_response.get('next_page') | |
122 | ||
123 | def _real_extract(self, url): | |
124 | user_id = self._match_id(url) | |
125 | user_info = self._download_json( | |
126 | self.USER_INFO_API_URL % user_id, user_id, | |
127 | note='Downloading user info', fatal=False) | |
128 | self.assert_error(user_info) | |
129 | ||
130 | uploader = user_info.get('name') | |
131 | description = user_info.get('description') | |
132 | ||
133 | entries = self._entries(user_id) | |
134 | return self.playlist_result(entries, user_id, uploader, description) |