3 from .common
import InfoExtractor
4 from ..compat
import compat_parse_qs
5 from ..dependencies
import websockets
18 class FC2IE(InfoExtractor
):
19 _VALID_URL
= r
'^(?:https?://video\.fc2\.com/(?:[^/]+/)*content/|fc2:)(?P<id>[^/]+)'
21 _NETRC_MACHINE
= 'fc2'
23 'url': 'http://video.fc2.com/en/content/20121103kUan1KHs',
24 'md5': 'a6ebe8ebe0396518689d963774a54eb7',
26 'id': '20121103kUan1KHs',
28 'title': 'Boxing again with Puff',
31 'url': 'http://video.fc2.com/en/content/20150125cEva0hDn/',
33 'id': '20150125cEva0hDn',
37 'username': 'ytdl@yt-dl.org',
40 'skip': 'requires actual password',
42 'url': 'http://video.fc2.com/en/a/content/20130926eZpARwsF',
43 'only_matching': True,
47 username
, password
= self
._get
_login
_info
()
48 if username
is None or password
is None:
59 login_data
= urlencode_postdata(login_form_strs
)
60 request
= sanitized_Request(
61 'https://secure.id.fc2.com/index.php?mode=login&switch_language=en', login_data
)
63 login_results
= self
._download
_webpage
(request
, None, note
='Logging in', errnote
='Unable to log in')
64 if 'mode=redirect&login=done' not in login_results
:
65 self
.report_warning('unable to log in: bad username or password')
69 login_redir
= sanitized_Request('http://id.fc2.com/?mode=redirect&login=done')
70 self
._download
_webpage
(
71 login_redir
, None, note
='Login redirect', errnote
='Login redirect failed')
75 def _real_extract(self
, url
):
76 video_id
= self
._match
_id
(url
)
79 if not url
.startswith('fc2:'):
80 webpage
= self
._download
_webpage
(url
, video_id
)
81 self
.cookiejar
.clear_session_cookies() # must clear
84 title
, thumbnail
, description
= None, None, None
85 if webpage
is not None:
86 title
= self
._html
_search
_regex
(
87 (r
'<h2\s+class="videoCnt_title">([^<]+?)</h2>',
88 r
'\s+href="[^"]+"\s*title="([^"]+?)"\s*rel="nofollow">\s*<img',
89 # there's two matches in the webpage
90 r
'\s+href="[^"]+"\s*title="([^"]+?)"\s*rel="nofollow">\s*\1'),
93 thumbnail
= self
._og
_search
_thumbnail
(webpage
)
94 description
= self
._og
_search
_description
(webpage
, default
=None)
96 vidplaylist
= self
._download
_json
(
97 'https://video.fc2.com/api/v3/videoplaylist/%s?sh=1&fs=0' % video_id
, video_id
,
98 note
='Downloading info page')
99 vid_url
= traverse_obj(vidplaylist
, ('playlist', 'nq'))
101 raise ExtractorError('Unable to extract video URL')
102 vid_url
= urljoin('https://video.fc2.com/', vid_url
)
109 'protocol': 'm3u8_native',
110 'description': description
,
111 'thumbnail': thumbnail
,
115 class FC2EmbedIE(InfoExtractor
):
116 _VALID_URL
= r
'https?://video\.fc2\.com/flv2\.swf\?(?P<query>.+)'
117 IE_NAME
= 'fc2:embed'
120 '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【吹替】',
121 'md5': 'b8aae5334cb691bdb1193a88a6ab5d5a',
123 'id': '201403223kCqB3Ez',
125 'title': 'プリズン・ブレイク S1-01 マイケル 【吹替】',
126 'thumbnail': r
're:^https?://.*\.jpg$',
130 def _real_extract(self
, url
):
131 mobj
= self
._match
_valid
_url
(url
)
132 query
= compat_parse_qs(mobj
.group('query'))
134 video_id
= query
['i'][-1]
135 title
= query
.get('tl', ['FC2 video %s' % video_id
])[0]
137 sj
= query
.get('sj', [None])[0]
140 # See thumbnailImagePath() in ServerConst.as of flv2.swf
141 thumbnail
= 'http://video%s-thumbnail.fc2.com/up/pic/%s.jpg' % (
142 sj
, '/'.join((video_id
[:6], video_id
[6:8], video_id
[-2], video_id
[-1], video_id
)))
145 '_type': 'url_transparent',
146 'ie_key': FC2IE
.ie_key(),
147 'url': 'fc2:%s' % video_id
,
149 'thumbnail': thumbnail
,
153 class FC2LiveIE(InfoExtractor
):
154 _VALID_URL
= r
'https?://live\.fc2\.com/(?P<id>\d+)'
158 'url': 'https://live.fc2.com/57892267/',
163 'uploader_id': '57892267',
164 'thumbnail': r
're:https?://.+fc2.+',
166 'skip': 'livestream',
169 def _real_extract(self
, url
):
171 raise ExtractorError('websockets library is not available. Please install it.', expected
=True)
172 video_id
= self
._match
_id
(url
)
173 webpage
= self
._download
_webpage
('https://live.fc2.com/%s/' % video_id
, video_id
)
175 self
._set
_cookie
('live.fc2.com', 'js-player_size', '1')
177 member_api
= self
._download
_json
(
178 'https://live.fc2.com/api/memberApi.php', video_id
, data
=urlencode_postdata({
183 }), note
='Requesting member info')
185 control_server
= self
._download
_json
(
186 'https://live.fc2.com/api/getControlServer.php', video_id
, note
='Downloading ControlServer data',
187 data
=urlencode_postdata({
188 'channel_id': video_id
,
191 'channel_version': member_api
['data']['channel_data']['version'],
192 'client_version': '2.1.0\n [1]',
194 'client_app': 'browser_hls',
196 }), headers
={'X-Requested-With': 'XMLHttpRequest'}
)
197 self
._set
_cookie
('live.fc2.com', 'l_ortkn', control_server
['orz_raw'])
199 ws_url
= update_url_query(control_server
['url'], {'control_token': control_server['control_token']}
)
202 self
.to_screen('%s: Fetching HLS playlist info via WebSocket' % video_id
)
203 ws
= WebSocketsWrapper(ws_url
, {
204 'Cookie': str(self
._get
_cookies
('https://live.fc2.com/'))[12:],
205 'Origin': 'https://live.fc2.com',
207 'User-Agent': self
.get_param('http_headers')['User-Agent'],
210 self
.write_debug('Sending HLS server request')
216 data
= self
._parse
_json
(recv
, video_id
, fatal
=False)
217 if not data
or not isinstance(data
, dict):
220 if data
.get('name') == 'connect_complete':
222 ws
.send(r
'{"name":"get_hls_information","arguments":{},"id":1}')
228 data
= self
._parse
_json
(recv
, video_id
, fatal
=False)
229 if not data
or not isinstance(data
, dict):
231 if data
.get('name') == '_response_' and data
.get('id') == 1:
232 self
.write_debug('Goodbye')
235 self
.write_debug('Server said: %s%s' % (recv
[:100], '...' if len(recv
) > 100 else ''))
237 if not playlist_data
:
238 raise ExtractorError('Unable to fetch HLS playlist info via WebSocket')
241 for name
, playlists
in playlist_data
['arguments'].items():
242 if not isinstance(playlists
, list):
245 if pl
.get('status') == 0 and 'master_playlist' in pl
.get('url'):
246 formats
.extend(self
._extract
_m
3u8_formats
(
247 pl
['url'], video_id
, ext
='mp4', m3u8_id
=name
, live
=True,
249 'Origin': 'https://live.fc2.com',
253 self
._sort
_formats
(formats
)
256 'protocol': 'fc2_live',
260 title
= self
._html
_search
_meta
(('og:title', 'twitter:title'), webpage
, 'live title', fatal
=False)
262 title
= self
._html
_extract
_title
(webpage
, 'html title', fatal
=False)
264 # remove service name in <title>
265 title
= re
.sub(r
'\s+-\s+.+$', '', title
)
268 match
= self
._search
_regex
(r
'^(.+?)\s*\[(.+?)\]$', title
, 'title and uploader', default
=None, group
=(1, 2))
269 if match
and all(match
):
270 title
, uploader
= match
272 live_info_view
= self
._search
_regex
(r
'(?s)liveInfoView\s*:\s*({.+?}),\s*premiumStateView', webpage
, 'user info', fatal
=False) or None
274 # remove jQuery code from object literal
275 live_info_view
= re
.sub(r
'\$\(.+?\)[^,]+,', '"",', live_info_view
)
276 live_info_view
= self
._parse
_json
(js_to_json(live_info_view
), video_id
)
280 'title': title
or traverse_obj(live_info_view
, 'title'),
281 'description': self
._html
_search
_meta
(
282 ('og:description', 'twitter:description'),
283 webpage
, 'live description', fatal
=False) or traverse_obj(live_info_view
, 'info'),
285 'uploader': uploader
or traverse_obj(live_info_view
, 'name'),
286 'uploader_id': video_id
,
287 'thumbnail': traverse_obj(live_info_view
, 'thumb'),