4 from .common
import InfoExtractor
5 from ..networking
import Request
16 class FC2IE(InfoExtractor
):
17 _VALID_URL
= r
'^(?:https?://video\.fc2\.com/(?:[^/]+/)*content/|fc2:)(?P<id>[^/]+)'
19 _NETRC_MACHINE
= 'fc2'
21 'url': 'http://video.fc2.com/en/content/20121103kUan1KHs',
22 'md5': 'a6ebe8ebe0396518689d963774a54eb7',
24 'id': '20121103kUan1KHs',
26 'title': 'Boxing again with Puff',
29 'url': 'http://video.fc2.com/en/content/20150125cEva0hDn/',
31 'id': '20150125cEva0hDn',
35 'username': 'ytdl@yt-dl.org',
38 'skip': 'requires actual password',
40 'url': 'http://video.fc2.com/en/a/content/20130926eZpARwsF',
41 'only_matching': True,
45 username
, password
= self
._get
_login
_info
()
46 if username
is None or password
is None:
57 login_data
= urlencode_postdata(login_form_strs
)
59 'https://secure.id.fc2.com/index.php?mode=login&switch_language=en', login_data
)
61 login_results
= self
._download
_webpage
(request
, None, note
='Logging in', errnote
='Unable to log in')
62 if 'mode=redirect&login=done' not in login_results
:
63 self
.report_warning('unable to log in: bad username or password')
67 login_redir
= Request('http://id.fc2.com/?mode=redirect&login=done')
68 self
._download
_webpage
(
69 login_redir
, None, note
='Login redirect', errnote
='Login redirect failed')
73 def _real_extract(self
, url
):
74 video_id
= self
._match
_id
(url
)
77 if not url
.startswith('fc2:'):
78 webpage
= self
._download
_webpage
(url
, video_id
)
79 self
.cookiejar
.clear_session_cookies() # must clear
82 title
, thumbnail
, description
= None, None, None
83 if webpage
is not None:
84 title
= self
._html
_search
_regex
(
85 (r
'<h2\s+class="videoCnt_title">([^<]+?)</h2>',
86 r
'\s+href="[^"]+"\s*title="([^"]+?)"\s*rel="nofollow">\s*<img',
87 # there's two matches in the webpage
88 r
'\s+href="[^"]+"\s*title="([^"]+?)"\s*rel="nofollow">\s*\1'),
91 thumbnail
= self
._og
_search
_thumbnail
(webpage
)
92 description
= self
._og
_search
_description
(webpage
, default
=None)
94 vidplaylist
= self
._download
_json
(
95 f
'https://video.fc2.com/api/v3/videoplaylist/{video_id}?sh=1&fs=0', video_id
,
96 note
='Downloading info page')
97 vid_url
= traverse_obj(vidplaylist
, ('playlist', 'nq'))
99 raise ExtractorError('Unable to extract video URL')
100 vid_url
= urljoin('https://video.fc2.com/', vid_url
)
107 'protocol': 'm3u8_native',
108 'description': description
,
109 'thumbnail': thumbnail
,
113 class FC2EmbedIE(InfoExtractor
):
114 _VALID_URL
= r
'https?://video\.fc2\.com/flv2\.swf\?(?P<query>.+)'
115 IE_NAME
= 'fc2:embed'
118 'url': 'http://video.fc2.com/flv2.swf?t=201404182936758512407645&i=20130316kwishtfitaknmcgd76kjd864hso93htfjcnaogz629mcgfs6rbfk0hsycma7shkf85937cbchfygd74&i=201403223kCqB3Ez&d=2625&sj=11&lang=ja&rel=1&from=11&cmt=1&tk=TlRBM09EQTNNekU9&tl=プリズン・ブレイク%20S1-01%20マイケル%20【吹替】',
119 'md5': 'b8aae5334cb691bdb1193a88a6ab5d5a',
121 'id': '201403223kCqB3Ez',
123 'title': 'プリズン・ブレイク S1-01 マイケル 【吹替】',
124 'thumbnail': r
're:^https?://.*\.jpg$',
128 def _real_extract(self
, url
):
129 mobj
= self
._match
_valid
_url
(url
)
130 query
= urllib
.parse
.parse_qs(mobj
.group('query'))
132 video_id
= query
['i'][-1]
133 title
= query
.get('tl', [f
'FC2 video {video_id}'])[0]
135 sj
= query
.get('sj', [None])[0]
138 # See thumbnailImagePath() in ServerConst.as of flv2.swf
139 thumbnail
= 'http://video{}-thumbnail.fc2.com/up/pic/{}.jpg'.format(
140 sj
, '/'.join((video_id
[:6], video_id
[6:8], video_id
[-2], video_id
[-1], video_id
)))
143 '_type': 'url_transparent',
144 'ie_key': FC2IE
.ie_key(),
145 'url': f
'fc2:{video_id}',
147 'thumbnail': thumbnail
,
151 class FC2LiveIE(InfoExtractor
):
152 _VALID_URL
= r
'https?://live\.fc2\.com/(?P<id>\d+)'
156 'url': 'https://live.fc2.com/57892267/',
161 'uploader_id': '57892267',
162 'thumbnail': r
're:https?://.+fc2.+',
164 'skip': 'livestream',
167 def _real_extract(self
, url
):
168 video_id
= self
._match
_id
(url
)
169 webpage
= self
._download
_webpage
(f
'https://live.fc2.com/{video_id}/', video_id
)
171 self
._set
_cookie
('live.fc2.com', 'js-player_size', '1')
173 member_api
= self
._download
_json
(
174 'https://live.fc2.com/api/memberApi.php', video_id
, data
=urlencode_postdata({
178 'streamid': video_id
,
179 }), note
='Requesting member info')
181 control_server
= self
._download
_json
(
182 'https://live.fc2.com/api/getControlServer.php', video_id
, note
='Downloading ControlServer data',
183 data
=urlencode_postdata({
184 'channel_id': video_id
,
187 'channel_version': member_api
['data']['channel_data']['version'],
188 'client_version': '2.1.0\n [1]',
190 'client_app': 'browser_hls',
192 }), headers
={'X-Requested-With': 'XMLHttpRequest'})
193 self
._set
_cookie
('live.fc2.com', 'l_ortkn', control_server
['orz_raw'])
195 ws_url
= update_url_query(control_server
['url'], {'control_token': control_server
['control_token']})
198 ws
= self
._request
_webpage
(Request(ws_url
, headers
={
199 'Origin': 'https://live.fc2.com',
200 }), video_id
, note
='Fetching HLS playlist info via WebSocket')
202 self
.write_debug('Sending HLS server request')
208 data
= self
._parse
_json
(recv
, video_id
, fatal
=False)
209 if not data
or not isinstance(data
, dict):
212 if data
.get('name') == 'connect_complete':
214 ws
.send(r
'{"name":"get_hls_information","arguments":{},"id":1}')
220 data
= self
._parse
_json
(recv
, video_id
, fatal
=False)
221 if not data
or not isinstance(data
, dict):
223 if data
.get('name') == '_response_' and data
.get('id') == 1:
224 self
.write_debug('Goodbye')
227 self
.write_debug('Server said: {}{}'.format(recv
[:100], '...' if len(recv
) > 100 else ''))
229 if not playlist_data
:
230 raise ExtractorError('Unable to fetch HLS playlist info via WebSocket')
233 for name
, playlists
in playlist_data
['arguments'].items():
234 if not isinstance(playlists
, list):
237 if pl
.get('status') == 0 and 'master_playlist' in pl
.get('url'):
238 formats
.extend(self
._extract
_m
3u8_formats
(
239 pl
['url'], video_id
, ext
='mp4', m3u8_id
=name
, live
=True,
241 'Origin': 'https://live.fc2.com',
247 'protocol': 'fc2_live',
251 title
= self
._html
_search
_meta
(('og:title', 'twitter:title'), webpage
, 'live title', fatal
=False)
253 title
= self
._html
_extract
_title
(webpage
, 'html title', fatal
=False)
255 # remove service name in <title>
256 title
= re
.sub(r
'\s+-\s+.+$', '', title
)
259 match
= self
._search
_regex
(r
'^(.+?)\s*\[(.+?)\]$', title
, 'title and uploader', default
=None, group
=(1, 2))
260 if match
and all(match
):
261 title
, uploader
= match
263 live_info_view
= self
._search
_regex
(r
'(?s)liveInfoView\s*:\s*({.+?}),\s*premiumStateView', webpage
, 'user info', fatal
=False) or None
265 # remove jQuery code from object literal
266 live_info_view
= re
.sub(r
'\$\(.+?\)[^,]+,', '"",', live_info_view
)
267 live_info_view
= self
._parse
_json
(js_to_json(live_info_view
), video_id
)
271 'title': title
or traverse_obj(live_info_view
, 'title'),
272 'description': self
._html
_search
_meta
(
273 ('og:description', 'twitter:description'),
274 webpage
, 'live description', fatal
=False) or traverse_obj(live_info_view
, 'info'),
276 'uploader': uploader
or traverse_obj(live_info_view
, 'name'),
277 'uploader_id': video_id
,
278 'thumbnail': traverse_obj(live_info_view
, 'thumb'),