2 from __future__
import unicode_literals
6 from .common
import InfoExtractor
24 class FC2IE(InfoExtractor
):
25 _VALID_URL
= r
'^(?:https?://video\.fc2\.com/(?:[^/]+/)*content/|fc2:)(?P<id>[^/]+)'
27 _NETRC_MACHINE
= 'fc2'
29 'url': 'http://video.fc2.com/en/content/20121103kUan1KHs',
30 'md5': 'a6ebe8ebe0396518689d963774a54eb7',
32 'id': '20121103kUan1KHs',
34 'title': 'Boxing again with Puff',
37 'url': 'http://video.fc2.com/en/content/20150125cEva0hDn/',
39 'id': '20150125cEva0hDn',
43 'username': 'ytdl@yt-dl.org',
46 'skip': 'requires actual password',
48 'url': 'http://video.fc2.com/en/a/content/20130926eZpARwsF',
49 'only_matching': True,
53 username
, password
= self
._get
_login
_info
()
54 if username
is None or password
is None:
65 login_data
= urlencode_postdata(login_form_strs
)
66 request
= sanitized_Request(
67 'https://secure.id.fc2.com/index.php?mode=login&switch_language=en', login_data
)
69 login_results
= self
._download
_webpage
(request
, None, note
='Logging in', errnote
='Unable to log in')
70 if 'mode=redirect&login=done' not in login_results
:
71 self
.report_warning('unable to log in: bad username or password')
75 login_redir
= sanitized_Request('http://id.fc2.com/?mode=redirect&login=done')
76 self
._download
_webpage
(
77 login_redir
, None, note
='Login redirect', errnote
='Login redirect failed')
81 def _real_extract(self
, url
):
82 video_id
= self
._match
_id
(url
)
85 if not url
.startswith('fc2:'):
86 webpage
= self
._download
_webpage
(url
, video_id
)
87 self
._downloader
.cookiejar
.clear_session_cookies() # must clear
90 title
, thumbnail
, description
= None, None, None
91 if webpage
is not None:
92 title
= self
._html
_search
_regex
(
93 (r
'<h2\s+class="videoCnt_title">([^<]+?)</h2>',
94 r
'\s+href="[^"]+"\s*title="([^"]+?)"\s*rel="nofollow">\s*<img',
95 # there's two matches in the webpage
96 r
'\s+href="[^"]+"\s*title="([^"]+?)"\s*rel="nofollow">\s*\1'),
99 thumbnail
= self
._og
_search
_thumbnail
(webpage
)
100 description
= self
._og
_search
_description
(webpage
, default
=None)
102 vidplaylist
= self
._download
_json
(
103 'https://video.fc2.com/api/v3/videoplaylist/%s?sh=1&fs=0' % video_id
, video_id
,
104 note
='Downloading info page')
105 vid_url
= traverse_obj(vidplaylist
, ('playlist', 'nq'))
107 raise ExtractorError('Unable to extract video URL')
108 vid_url
= urljoin('https://video.fc2.com/', vid_url
)
115 'protocol': 'm3u8_native',
116 'description': description
,
117 'thumbnail': thumbnail
,
121 class FC2EmbedIE(InfoExtractor
):
122 _VALID_URL
= r
'https?://video\.fc2\.com/flv2\.swf\?(?P<query>.+)'
123 IE_NAME
= 'fc2:embed'
126 '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【吹替】',
127 'md5': 'b8aae5334cb691bdb1193a88a6ab5d5a',
129 'id': '201403223kCqB3Ez',
131 'title': 'プリズン・ブレイク S1-01 マイケル 【吹替】',
132 'thumbnail': r
're:^https?://.*\.jpg$',
136 def _real_extract(self
, url
):
137 mobj
= self
._match
_valid
_url
(url
)
138 query
= compat_parse_qs(mobj
.group('query'))
140 video_id
= query
['i'][-1]
141 title
= query
.get('tl', ['FC2 video %s' % video_id
])[0]
143 sj
= query
.get('sj', [None])[0]
146 # See thumbnailImagePath() in ServerConst.as of flv2.swf
147 thumbnail
= 'http://video%s-thumbnail.fc2.com/up/pic/%s.jpg' % (
148 sj
, '/'.join((video_id
[:6], video_id
[6:8], video_id
[-2], video_id
[-1], video_id
)))
151 '_type': 'url_transparent',
152 'ie_key': FC2IE
.ie_key(),
153 'url': 'fc2:%s' % video_id
,
155 'thumbnail': thumbnail
,
159 class FC2LiveIE(InfoExtractor
):
160 _VALID_URL
= r
'https?://live\.fc2\.com/(?P<id>\d+)'
164 'url': 'https://live.fc2.com/57892267/',
169 'uploader_id': '57892267',
170 'thumbnail': r
're:https?://.+fc2.+',
172 'skip': 'livestream',
175 def _real_extract(self
, url
):
176 if not has_websockets
:
177 raise ExtractorError('websockets library is not available. Please install it.', expected
=True)
178 video_id
= self
._match
_id
(url
)
179 webpage
= self
._download
_webpage
('https://live.fc2.com/%s/' % video_id
, video_id
)
181 self
._set
_cookie
('live.fc2.com', 'js-player_size', '1')
183 member_api
= self
._download
_json
(
184 'https://live.fc2.com/api/memberApi.php', video_id
, data
=urlencode_postdata({
189 }), note
='Requesting member info')
191 control_server
= self
._download
_json
(
192 'https://live.fc2.com/api/getControlServer.php', video_id
, note
='Downloading ControlServer data',
193 data
=urlencode_postdata({
194 'channel_id': video_id
,
197 'channel_version': member_api
['data']['channel_data']['version'],
198 'client_version': '2.1.0\n [1]',
200 'client_app': 'browser_hls',
202 }), headers
={'X-Requested-With': 'XMLHttpRequest'}
)
203 self
._set
_cookie
('live.fc2.com', 'l_ortkn', control_server
['orz_raw'])
205 ws_url
= update_url_query(control_server
['url'], {'control_token': control_server['control_token']}
)
208 self
.to_screen('%s: Fetching HLS playlist info via WebSocket' % video_id
)
209 ws
= WebSocketsWrapper(ws_url
, {
210 'Cookie': str(self
._get
_cookies
('https://live.fc2.com/'))[12:],
211 'Origin': 'https://live.fc2.com',
213 'User-Agent': std_headers
['User-Agent'],
216 self
.write_debug('[debug] Sending HLS server request')
222 data
= self
._parse
_json
(recv
, video_id
, fatal
=False)
223 if not data
or not isinstance(data
, dict):
226 if data
.get('name') == 'connect_complete':
228 ws
.send(r
'{"name":"get_hls_information","arguments":{},"id":1}')
234 data
= self
._parse
_json
(recv
, video_id
, fatal
=False)
235 if not data
or not isinstance(data
, dict):
237 if data
.get('name') == '_response_' and data
.get('id') == 1:
238 self
.write_debug('[debug] Goodbye.')
241 elif self
._downloader
.params
.get('verbose', False):
243 recv
= recv
[:100] + '...'
244 self
.to_screen('[debug] Server said: %s' % recv
)
246 if not playlist_data
:
247 raise ExtractorError('Unable to fetch HLS playlist info via WebSocket')
250 for name
, playlists
in playlist_data
['arguments'].items():
251 if not isinstance(playlists
, list):
254 if pl
.get('status') == 0 and 'master_playlist' in pl
.get('url'):
255 formats
.extend(self
._extract
_m
3u8_formats
(
256 pl
['url'], video_id
, ext
='mp4', m3u8_id
=name
, live
=True,
258 'Origin': 'https://live.fc2.com',
262 self
._sort
_formats
(formats
)
265 'protocol': 'fc2_live',
269 title
= self
._html
_search
_meta
(('og:title', 'twitter:title'), webpage
, 'live title', fatal
=False)
271 title
= self
._html
_extract
_title
(webpage
, 'html title', fatal
=False)
273 # remove service name in <title>
274 title
= re
.sub(r
'\s+-\s+.+$', '', title
)
277 match
= self
._search
_regex
(r
'^(.+?)\s*\[(.+?)\]$', title
, 'title and uploader', default
=None, group
=(1, 2))
278 if match
and all(match
):
279 title
, uploader
= match
281 live_info_view
= self
._search
_regex
(r
'(?s)liveInfoView\s*:\s*({.+?}),\s*premiumStateView', webpage
, 'user info', fatal
=False) or None
283 # remove jQuery code from object literal
284 live_info_view
= re
.sub(r
'\$\(.+?\)[^,]+,', '"",', live_info_view
)
285 live_info_view
= self
._parse
_json
(js_to_json(live_info_view
), video_id
)
289 'title': title
or traverse_obj(live_info_view
, 'title'),
290 'description': self
._html
_search
_meta
(
291 ('og:description', 'twitter:description'),
292 webpage
, 'live description', fatal
=False) or traverse_obj(live_info_view
, 'info'),
294 'uploader': uploader
or traverse_obj(live_info_view
, 'name'),
295 'uploader_id': video_id
,
296 'thumbnail': traverse_obj(live_info_view
, 'thumb'),