3 from .common
import InfoExtractor
21 class FC2IE(InfoExtractor
):
22 _VALID_URL
= r
'^(?:https?://video\.fc2\.com/(?:[^/]+/)*content/|fc2:)(?P<id>[^/]+)'
24 _NETRC_MACHINE
= 'fc2'
26 'url': 'http://video.fc2.com/en/content/20121103kUan1KHs',
27 'md5': 'a6ebe8ebe0396518689d963774a54eb7',
29 'id': '20121103kUan1KHs',
31 'title': 'Boxing again with Puff',
34 'url': 'http://video.fc2.com/en/content/20150125cEva0hDn/',
36 'id': '20150125cEva0hDn',
40 'username': 'ytdl@yt-dl.org',
43 'skip': 'requires actual password',
45 'url': 'http://video.fc2.com/en/a/content/20130926eZpARwsF',
46 'only_matching': True,
50 username
, password
= self
._get
_login
_info
()
51 if username
is None or password
is None:
62 login_data
= urlencode_postdata(login_form_strs
)
63 request
= sanitized_Request(
64 'https://secure.id.fc2.com/index.php?mode=login&switch_language=en', login_data
)
66 login_results
= self
._download
_webpage
(request
, None, note
='Logging in', errnote
='Unable to log in')
67 if 'mode=redirect&login=done' not in login_results
:
68 self
.report_warning('unable to log in: bad username or password')
72 login_redir
= sanitized_Request('http://id.fc2.com/?mode=redirect&login=done')
73 self
._download
_webpage
(
74 login_redir
, None, note
='Login redirect', errnote
='Login redirect failed')
78 def _real_extract(self
, url
):
79 video_id
= self
._match
_id
(url
)
82 if not url
.startswith('fc2:'):
83 webpage
= self
._download
_webpage
(url
, video_id
)
84 self
._downloader
.cookiejar
.clear_session_cookies() # must clear
87 title
, thumbnail
, description
= None, None, None
88 if webpage
is not None:
89 title
= self
._html
_search
_regex
(
90 (r
'<h2\s+class="videoCnt_title">([^<]+?)</h2>',
91 r
'\s+href="[^"]+"\s*title="([^"]+?)"\s*rel="nofollow">\s*<img',
92 # there's two matches in the webpage
93 r
'\s+href="[^"]+"\s*title="([^"]+?)"\s*rel="nofollow">\s*\1'),
96 thumbnail
= self
._og
_search
_thumbnail
(webpage
)
97 description
= self
._og
_search
_description
(webpage
, default
=None)
99 vidplaylist
= self
._download
_json
(
100 'https://video.fc2.com/api/v3/videoplaylist/%s?sh=1&fs=0' % video_id
, video_id
,
101 note
='Downloading info page')
102 vid_url
= traverse_obj(vidplaylist
, ('playlist', 'nq'))
104 raise ExtractorError('Unable to extract video URL')
105 vid_url
= urljoin('https://video.fc2.com/', vid_url
)
112 'protocol': 'm3u8_native',
113 'description': description
,
114 'thumbnail': thumbnail
,
118 class FC2EmbedIE(InfoExtractor
):
119 _VALID_URL
= r
'https?://video\.fc2\.com/flv2\.swf\?(?P<query>.+)'
120 IE_NAME
= 'fc2:embed'
123 '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【吹替】',
124 'md5': 'b8aae5334cb691bdb1193a88a6ab5d5a',
126 'id': '201403223kCqB3Ez',
128 'title': 'プリズン・ブレイク S1-01 マイケル 【吹替】',
129 'thumbnail': r
're:^https?://.*\.jpg$',
133 def _real_extract(self
, url
):
134 mobj
= self
._match
_valid
_url
(url
)
135 query
= compat_parse_qs(mobj
.group('query'))
137 video_id
= query
['i'][-1]
138 title
= query
.get('tl', ['FC2 video %s' % video_id
])[0]
140 sj
= query
.get('sj', [None])[0]
143 # See thumbnailImagePath() in ServerConst.as of flv2.swf
144 thumbnail
= 'http://video%s-thumbnail.fc2.com/up/pic/%s.jpg' % (
145 sj
, '/'.join((video_id
[:6], video_id
[6:8], video_id
[-2], video_id
[-1], video_id
)))
148 '_type': 'url_transparent',
149 'ie_key': FC2IE
.ie_key(),
150 'url': 'fc2:%s' % video_id
,
152 'thumbnail': thumbnail
,
156 class FC2LiveIE(InfoExtractor
):
157 _VALID_URL
= r
'https?://live\.fc2\.com/(?P<id>\d+)'
161 'url': 'https://live.fc2.com/57892267/',
166 'uploader_id': '57892267',
167 'thumbnail': r
're:https?://.+fc2.+',
169 'skip': 'livestream',
172 def _real_extract(self
, url
):
173 if not has_websockets
:
174 raise ExtractorError('websockets library is not available. Please install it.', expected
=True)
175 video_id
= self
._match
_id
(url
)
176 webpage
= self
._download
_webpage
('https://live.fc2.com/%s/' % video_id
, video_id
)
178 self
._set
_cookie
('live.fc2.com', 'js-player_size', '1')
180 member_api
= self
._download
_json
(
181 'https://live.fc2.com/api/memberApi.php', video_id
, data
=urlencode_postdata({
186 }), note
='Requesting member info')
188 control_server
= self
._download
_json
(
189 'https://live.fc2.com/api/getControlServer.php', video_id
, note
='Downloading ControlServer data',
190 data
=urlencode_postdata({
191 'channel_id': video_id
,
194 'channel_version': member_api
['data']['channel_data']['version'],
195 'client_version': '2.1.0\n [1]',
197 'client_app': 'browser_hls',
199 }), headers
={'X-Requested-With': 'XMLHttpRequest'}
)
200 self
._set
_cookie
('live.fc2.com', 'l_ortkn', control_server
['orz_raw'])
202 ws_url
= update_url_query(control_server
['url'], {'control_token': control_server['control_token']}
)
205 self
.to_screen('%s: Fetching HLS playlist info via WebSocket' % video_id
)
206 ws
= WebSocketsWrapper(ws_url
, {
207 'Cookie': str(self
._get
_cookies
('https://live.fc2.com/'))[12:],
208 'Origin': 'https://live.fc2.com',
210 'User-Agent': std_headers
['User-Agent'],
213 self
.write_debug('[debug] Sending HLS server request')
219 data
= self
._parse
_json
(recv
, video_id
, fatal
=False)
220 if not data
or not isinstance(data
, dict):
223 if data
.get('name') == 'connect_complete':
225 ws
.send(r
'{"name":"get_hls_information","arguments":{},"id":1}')
231 data
= self
._parse
_json
(recv
, video_id
, fatal
=False)
232 if not data
or not isinstance(data
, dict):
234 if data
.get('name') == '_response_' and data
.get('id') == 1:
235 self
.write_debug('[debug] Goodbye.')
238 elif self
._downloader
.params
.get('verbose', False):
240 recv
= recv
[:100] + '...'
241 self
.to_screen('[debug] Server said: %s' % recv
)
243 if not playlist_data
:
244 raise ExtractorError('Unable to fetch HLS playlist info via WebSocket')
247 for name
, playlists
in playlist_data
['arguments'].items():
248 if not isinstance(playlists
, list):
251 if pl
.get('status') == 0 and 'master_playlist' in pl
.get('url'):
252 formats
.extend(self
._extract
_m
3u8_formats
(
253 pl
['url'], video_id
, ext
='mp4', m3u8_id
=name
, live
=True,
255 'Origin': 'https://live.fc2.com',
259 self
._sort
_formats
(formats
)
262 'protocol': 'fc2_live',
266 title
= self
._html
_search
_meta
(('og:title', 'twitter:title'), webpage
, 'live title', fatal
=False)
268 title
= self
._html
_extract
_title
(webpage
, 'html title', fatal
=False)
270 # remove service name in <title>
271 title
= re
.sub(r
'\s+-\s+.+$', '', title
)
274 match
= self
._search
_regex
(r
'^(.+?)\s*\[(.+?)\]$', title
, 'title and uploader', default
=None, group
=(1, 2))
275 if match
and all(match
):
276 title
, uploader
= match
278 live_info_view
= self
._search
_regex
(r
'(?s)liveInfoView\s*:\s*({.+?}),\s*premiumStateView', webpage
, 'user info', fatal
=False) or None
280 # remove jQuery code from object literal
281 live_info_view
= re
.sub(r
'\$\(.+?\)[^,]+,', '"",', live_info_view
)
282 live_info_view
= self
._parse
_json
(js_to_json(live_info_view
), video_id
)
286 'title': title
or traverse_obj(live_info_view
, 'title'),
287 'description': self
._html
_search
_meta
(
288 ('og:description', 'twitter:description'),
289 webpage
, 'live description', fatal
=False) or traverse_obj(live_info_view
, 'info'),
291 'uploader': uploader
or traverse_obj(live_info_view
, 'name'),
292 'uploader_id': video_id
,
293 'thumbnail': traverse_obj(live_info_view
, 'thumb'),