3 from .common
import InfoExtractor
7 from ..dependencies
import websockets
20 class FC2IE(InfoExtractor
):
21 _VALID_URL
= r
'^(?:https?://video\.fc2\.com/(?:[^/]+/)*content/|fc2:)(?P<id>[^/]+)'
23 _NETRC_MACHINE
= 'fc2'
25 'url': 'http://video.fc2.com/en/content/20121103kUan1KHs',
26 'md5': 'a6ebe8ebe0396518689d963774a54eb7',
28 'id': '20121103kUan1KHs',
30 'title': 'Boxing again with Puff',
33 'url': 'http://video.fc2.com/en/content/20150125cEva0hDn/',
35 'id': '20150125cEva0hDn',
39 'username': 'ytdl@yt-dl.org',
42 'skip': 'requires actual password',
44 'url': 'http://video.fc2.com/en/a/content/20130926eZpARwsF',
45 'only_matching': True,
49 username
, password
= self
._get
_login
_info
()
50 if username
is None or password
is None:
61 login_data
= urlencode_postdata(login_form_strs
)
62 request
= sanitized_Request(
63 'https://secure.id.fc2.com/index.php?mode=login&switch_language=en', login_data
)
65 login_results
= self
._download
_webpage
(request
, None, note
='Logging in', errnote
='Unable to log in')
66 if 'mode=redirect&login=done' not in login_results
:
67 self
.report_warning('unable to log in: bad username or password')
71 login_redir
= sanitized_Request('http://id.fc2.com/?mode=redirect&login=done')
72 self
._download
_webpage
(
73 login_redir
, None, note
='Login redirect', errnote
='Login redirect failed')
77 def _real_extract(self
, url
):
78 video_id
= self
._match
_id
(url
)
81 if not url
.startswith('fc2:'):
82 webpage
= self
._download
_webpage
(url
, video_id
)
83 self
._downloader
.cookiejar
.clear_session_cookies() # must clear
86 title
, thumbnail
, description
= None, None, None
87 if webpage
is not None:
88 title
= self
._html
_search
_regex
(
89 (r
'<h2\s+class="videoCnt_title">([^<]+?)</h2>',
90 r
'\s+href="[^"]+"\s*title="([^"]+?)"\s*rel="nofollow">\s*<img',
91 # there's two matches in the webpage
92 r
'\s+href="[^"]+"\s*title="([^"]+?)"\s*rel="nofollow">\s*\1'),
95 thumbnail
= self
._og
_search
_thumbnail
(webpage
)
96 description
= self
._og
_search
_description
(webpage
, default
=None)
98 vidplaylist
= self
._download
_json
(
99 'https://video.fc2.com/api/v3/videoplaylist/%s?sh=1&fs=0' % video_id
, video_id
,
100 note
='Downloading info page')
101 vid_url
= traverse_obj(vidplaylist
, ('playlist', 'nq'))
103 raise ExtractorError('Unable to extract video URL')
104 vid_url
= urljoin('https://video.fc2.com/', vid_url
)
111 'protocol': 'm3u8_native',
112 'description': description
,
113 'thumbnail': thumbnail
,
117 class FC2EmbedIE(InfoExtractor
):
118 _VALID_URL
= r
'https?://video\.fc2\.com/flv2\.swf\?(?P<query>.+)'
119 IE_NAME
= 'fc2:embed'
122 '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【吹替】',
123 'md5': 'b8aae5334cb691bdb1193a88a6ab5d5a',
125 'id': '201403223kCqB3Ez',
127 'title': 'プリズン・ブレイク S1-01 マイケル 【吹替】',
128 'thumbnail': r
're:^https?://.*\.jpg$',
132 def _real_extract(self
, url
):
133 mobj
= self
._match
_valid
_url
(url
)
134 query
= compat_parse_qs(mobj
.group('query'))
136 video_id
= query
['i'][-1]
137 title
= query
.get('tl', ['FC2 video %s' % video_id
])[0]
139 sj
= query
.get('sj', [None])[0]
142 # See thumbnailImagePath() in ServerConst.as of flv2.swf
143 thumbnail
= 'http://video%s-thumbnail.fc2.com/up/pic/%s.jpg' % (
144 sj
, '/'.join((video_id
[:6], video_id
[6:8], video_id
[-2], video_id
[-1], video_id
)))
147 '_type': 'url_transparent',
148 'ie_key': FC2IE
.ie_key(),
149 'url': 'fc2:%s' % video_id
,
151 'thumbnail': thumbnail
,
155 class FC2LiveIE(InfoExtractor
):
156 _VALID_URL
= r
'https?://live\.fc2\.com/(?P<id>\d+)'
160 'url': 'https://live.fc2.com/57892267/',
165 'uploader_id': '57892267',
166 'thumbnail': r
're:https?://.+fc2.+',
168 'skip': 'livestream',
171 def _real_extract(self
, url
):
173 raise ExtractorError('websockets library is not available. Please install it.', expected
=True)
174 video_id
= self
._match
_id
(url
)
175 webpage
= self
._download
_webpage
('https://live.fc2.com/%s/' % video_id
, video_id
)
177 self
._set
_cookie
('live.fc2.com', 'js-player_size', '1')
179 member_api
= self
._download
_json
(
180 'https://live.fc2.com/api/memberApi.php', video_id
, data
=urlencode_postdata({
185 }), note
='Requesting member info')
187 control_server
= self
._download
_json
(
188 'https://live.fc2.com/api/getControlServer.php', video_id
, note
='Downloading ControlServer data',
189 data
=urlencode_postdata({
190 'channel_id': video_id
,
193 'channel_version': member_api
['data']['channel_data']['version'],
194 'client_version': '2.1.0\n [1]',
196 'client_app': 'browser_hls',
198 }), headers
={'X-Requested-With': 'XMLHttpRequest'}
)
199 self
._set
_cookie
('live.fc2.com', 'l_ortkn', control_server
['orz_raw'])
201 ws_url
= update_url_query(control_server
['url'], {'control_token': control_server['control_token']}
)
204 self
.to_screen('%s: Fetching HLS playlist info via WebSocket' % video_id
)
205 ws
= WebSocketsWrapper(ws_url
, {
206 'Cookie': str(self
._get
_cookies
('https://live.fc2.com/'))[12:],
207 'Origin': 'https://live.fc2.com',
209 'User-Agent': self
.get_param('http_headers')['User-Agent'],
212 self
.write_debug('[debug] Sending HLS server request')
218 data
= self
._parse
_json
(recv
, video_id
, fatal
=False)
219 if not data
or not isinstance(data
, dict):
222 if data
.get('name') == 'connect_complete':
224 ws
.send(r
'{"name":"get_hls_information","arguments":{},"id":1}')
230 data
= self
._parse
_json
(recv
, video_id
, fatal
=False)
231 if not data
or not isinstance(data
, dict):
233 if data
.get('name') == '_response_' and data
.get('id') == 1:
234 self
.write_debug('[debug] Goodbye.')
237 elif self
._downloader
.params
.get('verbose', False):
239 recv
= recv
[:100] + '...'
240 self
.to_screen('[debug] Server said: %s' % recv
)
242 if not playlist_data
:
243 raise ExtractorError('Unable to fetch HLS playlist info via WebSocket')
246 for name
, playlists
in playlist_data
['arguments'].items():
247 if not isinstance(playlists
, list):
250 if pl
.get('status') == 0 and 'master_playlist' in pl
.get('url'):
251 formats
.extend(self
._extract
_m
3u8_formats
(
252 pl
['url'], video_id
, ext
='mp4', m3u8_id
=name
, live
=True,
254 'Origin': 'https://live.fc2.com',
258 self
._sort
_formats
(formats
)
261 'protocol': 'fc2_live',
265 title
= self
._html
_search
_meta
(('og:title', 'twitter:title'), webpage
, 'live title', fatal
=False)
267 title
= self
._html
_extract
_title
(webpage
, 'html title', fatal
=False)
269 # remove service name in <title>
270 title
= re
.sub(r
'\s+-\s+.+$', '', title
)
273 match
= self
._search
_regex
(r
'^(.+?)\s*\[(.+?)\]$', title
, 'title and uploader', default
=None, group
=(1, 2))
274 if match
and all(match
):
275 title
, uploader
= match
277 live_info_view
= self
._search
_regex
(r
'(?s)liveInfoView\s*:\s*({.+?}),\s*premiumStateView', webpage
, 'user info', fatal
=False) or None
279 # remove jQuery code from object literal
280 live_info_view
= re
.sub(r
'\$\(.+?\)[^,]+,', '"",', live_info_view
)
281 live_info_view
= self
._parse
_json
(js_to_json(live_info_view
), video_id
)
285 'title': title
or traverse_obj(live_info_view
, 'title'),
286 'description': self
._html
_search
_meta
(
287 ('og:description', 'twitter:description'),
288 webpage
, 'live description', fatal
=False) or traverse_obj(live_info_view
, 'info'),
290 'uploader': uploader
or traverse_obj(live_info_view
, 'name'),
291 'uploader_id': video_id
,
292 'thumbnail': traverse_obj(live_info_view
, 'thumb'),