3 from .common
import InfoExtractor
12 class EplusIbIE(InfoExtractor
):
13 _NETRC_MACHINE
= '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+)']
19 'url': 'https://live.eplus.jp/ex/player?ib=YEFxb3Vyc2Dombnjg7blkrLlrablnJLjgrnjgq%2Fjg7zjg6vjgqLjgqTjg4njg6vlkIzlpb3kvJpgTGllbGxhIQ%3D%3D',
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,
29 'skip_download': True,
30 'ignore_no_formats_error': True,
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',
38 'url': 'https://live.eplus.jp/sample',
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,
47 'skip_download': True,
48 'ignore_no_formats_error': True,
50 'expected_warnings': [
51 'Could not find the playlist URL. This event may not be accessible',
52 'No video formats found!',
53 'Requested format is not available',
54 'This video is DRM protected',
57 'url': 'https://live.eplus.jp/2053935',
59 'id': '331320-0001-001',
60 'title': '丘みどり2020配信LIVE Vol.2 ~秋麗~ 【Streaming+(配信チケット)】',
61 'live_status': 'was_live',
62 'release_date': '20200920',
63 'release_timestamp': 1600596000,
66 'skip_download': True,
67 'ignore_no_formats_error': True,
69 'expected_warnings': [
70 'Could not find the playlist URL. This event may not be accessible',
71 'No video formats found!',
72 'Requested format is not available',
76 _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 def _login(self
, username
, password
, urlh
):
79 if not self
._get
_cookies
('https://live.eplus.jp/').get('ci_session'):
80 raise ExtractorError('Unable to get ci_session cookie')
82 cltft_token
= urlh
.headers
.get('X-CLTFT-Token')
84 raise ExtractorError('Unable to get X-CLTFT-Token')
85 self
._set
_cookie
('live.eplus.jp', 'X-CLTFT-Token', cltft_token
)
87 login_json
= self
._download
_json
(
88 'https://live.eplus.jp/member/api/v1/FTAuth/idpw', None,
89 note
='Sending pre-login info', errnote
='Unable to send pre-login info', headers
={
90 'Content-Type': 'application/json; charset=UTF-8',
92 'X-Cltft-Token': cltft_token
,
96 'loginPassword': password
,
98 if not login_json
.get('isSuccess'):
99 raise ExtractorError('Login failed: Invalid id or password', expected
=True)
101 self
._request
_webpage
(
102 urlh
.url
, None, note
='Logging in', errnote
='Unable to log in',
103 data
=urlencode_postdata({
105 'loginPassword': password
,
106 'Token.Default': cltft_token
,
108 }), headers
={'Referer': urlh.url}
)
110 def _real_extract(self
, url
):
111 video_id
= self
._match
_id
(url
)
112 webpage
, urlh
= self
._download
_webpage
_handle
(
113 url
, video_id
, headers
={'User-Agent': self._USER_AGENT}
)
114 if urlh
.url
.startswith('https://live.eplus.jp/member/auth'):
115 username
, password
= self
._get
_login
_info
()
117 self
.raise_login_required()
118 self
._login
(username
, password
, urlh
)
119 webpage
= self
._download
_webpage
(
120 url
, video_id
, headers
={'User-Agent': self._USER_AGENT}
)
122 data_json
= self
._search
_json
(r
'<script>\s*var app\s*=', webpage
, 'data json', video_id
)
124 if data_json
.get('drm_mode') == 'ON':
125 self
.report_drm(video_id
)
127 delivery_status
= data_json
.get('delivery_status')
128 archive_mode
= data_json
.get('archive_mode')
129 release_timestamp
= try_call(lambda: unified_timestamp(data_json
['event_datetime']) - 32400)
130 release_timestamp_str
= data_json
.get('event_datetime_text') # JST
132 self
.write_debug(f
'delivery_status = {delivery_status}, archive_mode = {archive_mode}')
134 if delivery_status
== 'PREPARING':
135 live_status
= 'is_upcoming'
136 elif delivery_status
== 'STARTED':
137 live_status
= 'is_live'
138 elif delivery_status
== 'STOPPED':
139 if archive_mode
!= 'ON':
140 raise ExtractorError(
141 'This event has ended and there is no archive for this event', expected
=True)
142 live_status
= 'post_live'
143 elif delivery_status
== 'WAIT_CONFIRM_ARCHIVED':
144 live_status
= 'post_live'
145 elif delivery_status
== 'CONFIRMED_ARCHIVE':
146 live_status
= 'was_live'
148 self
.report_warning(f
'Unknown delivery_status {delivery_status}, treat it as a live')
149 live_status
= 'is_live'
153 m3u8_playlist_urls
= self
._search
_json
(
154 r
'var\s+listChannels\s*=', webpage
, 'hls URLs', video_id
, contains_pattern
=r
'\[.+\]', default
=[])
155 if not m3u8_playlist_urls
:
156 if live_status
== 'is_upcoming':
157 self
.raise_no_formats(
158 f
'Could not find the playlist URL. This live event will begin at {release_timestamp_str} JST', expected
=True)
160 self
.raise_no_formats(
161 'Could not find the playlist URL. This event may not be accessible', expected
=True)
162 elif live_status
== 'is_upcoming':
163 self
.raise_no_formats(f
'This live event will begin at {release_timestamp_str} JST', expected
=True)
164 elif live_status
== 'post_live':
165 self
.raise_no_formats('This event has ended, and the archive will be available shortly', expected
=True)
167 for m3u8_playlist_url
in m3u8_playlist_urls
:
168 formats
.extend(self
._extract
_m
3u8_formats
(m3u8_playlist_url
, video_id
))
169 # FIXME: HTTP request headers need to be updated to continue download
170 warning
= 'Due to technical limitations, the download will be interrupted after one hour'
171 if live_status
== 'is_live':
172 self
.report_warning(warning
)
173 elif live_status
== 'was_live':
174 self
.report_warning(f
'{warning}. You can restart to continue the download')
177 'id': data_json
['app_id'],
178 'title': data_json
.get('app_name'),
180 'live_status': live_status
,
181 'description': data_json
.get('content'),
182 'release_timestamp': release_timestamp
,