]>
Commit | Line | Data |
---|---|---|
d5d1517e M |
1 | import json |
2 | ||
295fbb3a M |
3 | from .common import InfoExtractor |
4 | from ..utils import ( | |
5 | ExtractorError, | |
6 | try_call, | |
7 | unified_timestamp, | |
d5d1517e | 8 | urlencode_postdata, |
295fbb3a M |
9 | ) |
10 | ||
11 | ||
12 | class EplusIbIE(InfoExtractor): | |
d5d1517e M |
13 | _NETRC_MACHINE = 'eplus' |
14 | IE_NAME = 'eplus' | |
15 | IE_DESC = 'e+ (イープラス)' | |
16 | _VALID_URL = [r'https?://live\.eplus\.jp/ex/player\?ib=(?P<id>(?:\w|%2B|%2F){86}%3D%3D)', | |
17 | r'https?://live\.eplus\.jp/(?P<id>sample|\d+)'] | |
295fbb3a M |
18 | _TESTS = [{ |
19 | 'url': 'https://live.eplus.jp/ex/player?ib=YEFxb3Vyc2Dombnjg7blkrLlrablnJLjgrnjgq%2Fjg7zjg6vjgqLjgqTjg4njg6vlkIzlpb3kvJpgTGllbGxhIQ%3D%3D', | |
20 | 'info_dict': { | |
21 | 'id': '354502-0001-002', | |
22 | 'title': 'LoveLive!Series Presents COUNTDOWN LoveLive! 2021→2022~LIVE with a smile!~【Streaming+(配信)】', | |
23 | 'live_status': 'was_live', | |
24 | 'release_date': '20211231', | |
25 | 'release_timestamp': 1640952000, | |
26 | 'description': str, | |
27 | }, | |
28 | 'params': { | |
29 | 'skip_download': True, | |
30 | 'ignore_no_formats_error': True, | |
31 | }, | |
32 | 'expected_warnings': [ | |
33 | 'Could not find the playlist URL. This event may not be accessible', | |
34 | 'No video formats found!', | |
35 | 'Requested format is not available', | |
36 | ], | |
d5d1517e M |
37 | }, { |
38 | 'url': 'https://live.eplus.jp/sample', | |
39 | 'info_dict': { | |
40 | 'id': 'stream1ng20210719-test-005', | |
41 | 'title': 'Online streaming test for DRM', | |
42 | 'live_status': 'was_live', | |
43 | 'release_date': '20210719', | |
44 | 'release_timestamp': 1626703200, | |
45 | 'description': None, | |
46 | }, | |
47 | 'params': { | |
48 | 'skip_download': True, | |
49 | 'ignore_no_formats_error': True, | |
50 | }, | |
51 | 'expected_warnings': [ | |
52 | 'Could not find the playlist URL. This event may not be accessible', | |
53 | 'No video formats found!', | |
54 | 'Requested format is not available', | |
55 | 'This video is DRM protected', | |
56 | ], | |
57 | }, { | |
58 | 'url': 'https://live.eplus.jp/2053935', | |
59 | 'info_dict': { | |
60 | 'id': '331320-0001-001', | |
61 | 'title': '丘みどり2020配信LIVE Vol.2 ~秋麗~ 【Streaming+(配信チケット)】', | |
62 | 'live_status': 'was_live', | |
63 | 'release_date': '20200920', | |
64 | 'release_timestamp': 1600596000, | |
65 | }, | |
66 | 'params': { | |
67 | 'skip_download': True, | |
68 | 'ignore_no_formats_error': True, | |
69 | }, | |
70 | 'expected_warnings': [ | |
71 | 'Could not find the playlist URL. This event may not be accessible', | |
72 | 'No video formats found!', | |
73 | 'Requested format is not available', | |
74 | ], | |
295fbb3a M |
75 | }] |
76 | ||
d5d1517e M |
77 | _USER_AGENT = 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/119.0.0.0 Safari/537.36 Edg/119.0.0.0' |
78 | ||
79 | def _login(self, username, password, urlh): | |
80 | if not self._get_cookies('https://live.eplus.jp/').get('ci_session'): | |
81 | raise ExtractorError('Unable to get ci_session cookie') | |
82 | ||
83 | cltft_token = urlh.headers.get('X-CLTFT-Token') | |
84 | if not cltft_token: | |
85 | raise ExtractorError('Unable to get X-CLTFT-Token') | |
86 | self._set_cookie('live.eplus.jp', 'X-CLTFT-Token', cltft_token) | |
87 | ||
88 | login_json = self._download_json( | |
89 | 'https://live.eplus.jp/member/api/v1/FTAuth/idpw', None, | |
90 | note='Sending pre-login info', errnote='Unable to send pre-login info', headers={ | |
91 | 'Content-Type': 'application/json; charset=UTF-8', | |
92 | 'Referer': urlh.url, | |
93 | 'X-Cltft-Token': cltft_token, | |
94 | 'Accept': '*/*', | |
95 | }, data=json.dumps({ | |
96 | 'loginId': username, | |
97 | 'loginPassword': password, | |
98 | }).encode()) | |
99 | if not login_json.get('isSuccess'): | |
100 | raise ExtractorError('Login failed: Invalid id or password', expected=True) | |
101 | ||
102 | self._request_webpage( | |
103 | urlh.url, None, note='Logging in', errnote='Unable to log in', | |
104 | data=urlencode_postdata({ | |
105 | 'loginId': username, | |
106 | 'loginPassword': password, | |
107 | 'Token.Default': cltft_token, | |
108 | 'op': 'nextPage', | |
109 | }), headers={'Referer': urlh.url}) | |
110 | ||
295fbb3a M |
111 | def _real_extract(self, url): |
112 | video_id = self._match_id(url) | |
d5d1517e M |
113 | webpage, urlh = self._download_webpage_handle( |
114 | url, video_id, headers={'User-Agent': self._USER_AGENT}) | |
115 | if urlh.url.startswith('https://live.eplus.jp/member/auth'): | |
116 | username, password = self._get_login_info() | |
117 | if not username: | |
118 | self.raise_login_required() | |
119 | self._login(username, password, urlh) | |
120 | webpage = self._download_webpage( | |
121 | url, video_id, headers={'User-Agent': self._USER_AGENT}) | |
295fbb3a M |
122 | |
123 | data_json = self._search_json(r'<script>\s*var app\s*=', webpage, 'data json', video_id) | |
124 | ||
d5d1517e M |
125 | if data_json.get('drm_mode') == 'ON': |
126 | self.report_drm(video_id) | |
127 | ||
295fbb3a M |
128 | delivery_status = data_json.get('delivery_status') |
129 | archive_mode = data_json.get('archive_mode') | |
130 | release_timestamp = try_call(lambda: unified_timestamp(data_json['event_datetime']) - 32400) | |
131 | release_timestamp_str = data_json.get('event_datetime_text') # JST | |
132 | ||
133 | self.write_debug(f'delivery_status = {delivery_status}, archive_mode = {archive_mode}') | |
134 | ||
135 | if delivery_status == 'PREPARING': | |
136 | live_status = 'is_upcoming' | |
137 | elif delivery_status == 'STARTED': | |
138 | live_status = 'is_live' | |
139 | elif delivery_status == 'STOPPED': | |
140 | if archive_mode != 'ON': | |
141 | raise ExtractorError( | |
142 | 'This event has ended and there is no archive for this event', expected=True) | |
143 | live_status = 'post_live' | |
144 | elif delivery_status == 'WAIT_CONFIRM_ARCHIVED': | |
145 | live_status = 'post_live' | |
146 | elif delivery_status == 'CONFIRMED_ARCHIVE': | |
147 | live_status = 'was_live' | |
148 | else: | |
149 | self.report_warning(f'Unknown delivery_status {delivery_status}, treat it as a live') | |
150 | live_status = 'is_live' | |
151 | ||
152 | formats = [] | |
153 | ||
154 | m3u8_playlist_urls = self._search_json( | |
d5d1517e | 155 | r'var\s+listChannels\s*=', webpage, 'hls URLs', video_id, contains_pattern=r'\[.+\]', default=[]) |
295fbb3a M |
156 | if not m3u8_playlist_urls: |
157 | if live_status == 'is_upcoming': | |
158 | self.raise_no_formats( | |
159 | f'Could not find the playlist URL. This live event will begin at {release_timestamp_str} JST', expected=True) | |
160 | else: | |
161 | self.raise_no_formats( | |
162 | 'Could not find the playlist URL. This event may not be accessible', expected=True) | |
163 | elif live_status == 'is_upcoming': | |
164 | self.raise_no_formats(f'This live event will begin at {release_timestamp_str} JST', expected=True) | |
165 | elif live_status == 'post_live': | |
166 | self.raise_no_formats('This event has ended, and the archive will be available shortly', expected=True) | |
167 | else: | |
168 | for m3u8_playlist_url in m3u8_playlist_urls: | |
169 | formats.extend(self._extract_m3u8_formats(m3u8_playlist_url, video_id)) | |
170 | # FIXME: HTTP request headers need to be updated to continue download | |
171 | warning = 'Due to technical limitations, the download will be interrupted after one hour' | |
172 | if live_status == 'is_live': | |
173 | self.report_warning(warning) | |
174 | elif live_status == 'was_live': | |
175 | self.report_warning(f'{warning}. You can restart to continue the download') | |
176 | ||
177 | return { | |
178 | 'id': data_json['app_id'], | |
179 | 'title': data_json.get('app_name'), | |
180 | 'formats': formats, | |
181 | 'live_status': live_status, | |
182 | 'description': data_json.get('content'), | |
183 | 'release_timestamp': release_timestamp, | |
184 | } |