[A-Za-z0-9-]+/?)'
+ _TESTS = [{
+ 'url': 'https://thisvid.com/videos/french-boy-pantsed/',
+ 'md5': '3397979512c682f6b85b3b04989df224',
+ 'info_dict': {
+ 'id': '2400174',
+ 'ext': 'mp4',
+ 'title': 'French Boy Pantsed',
+ 'thumbnail': 'https://media.thisvid.com/contents/videos_screenshots/2400000/2400174/preview.mp4.jpg',
+ 'age_limit': 18,
+ }
+ }, {
+ 'url': 'https://thisvid.com/embed/2400174/',
+ 'md5': '3397979512c682f6b85b3b04989df224',
+ 'info_dict': {
+ 'id': '2400174',
+ 'ext': 'mp4',
+ 'title': 'French Boy Pantsed',
+ 'thumbnail': 'https://media.thisvid.com/contents/videos_screenshots/2400000/2400174/preview.mp4.jpg',
+ 'age_limit': 18,
+ }
+ }]
+
+ def _real_extract(self, url):
+ main_id = self._match_id(url)
+ webpage = self._download_webpage(url, main_id)
+
+ # URL decryptor was reversed from version 4.0.4, later verified working with 5.2.0 and may change in the future.
+ kvs_version = self._html_search_regex(r''
+ PARAMS_FIELD = 'params'
- VIDEO_PARAMS_RE = r'\bvlive\.video\.init\(([^)]+)'
- VIDEO_PARAMS_FIELD = 'video params'
+ params = self._search_regex(
+ PARAMS_RE, webpage, PARAMS_FIELD, default='', flags=re.DOTALL)
+ params = self._parse_json(params, working_id, fatal=False)
- params = self._parse_json(self._search_regex(
- VIDEO_PARAMS_RE, webpage, VIDEO_PARAMS_FIELD, default=''), video_id,
- transform_source=lambda s: '[' + s + ']', fatal=False)
+ video_params = try_get(params, lambda x: x["postDetail"]["post"]["officialVideo"], dict)
- if not params or len(params) < 7:
- params = self._search_regex(
- VIDEO_PARAMS_RE, webpage, VIDEO_PARAMS_FIELD)
- params = [p.strip(r'"') for p in re.split(r'\s*,\s*', params)]
+ if video_params is None:
+ error = try_get(params, lambda x: x["postDetail"]["error"], dict)
+ error_data = try_get(error, lambda x: x["data"], dict)
+ error_video = try_get(error_data, lambda x: x["officialVideo"], dict)
+ error_msg = try_get(error, lambda x: x["message"], compat_str)
+ product_type = try_get(error_data,
+ [lambda x: x["officialVideo"]["productType"],
+ lambda x: x["board"]["boardType"]],
+ compat_str)
- status, long_video_id, key = params[2], params[5], params[6]
- status = remove_start(status, 'PRODUCT_')
+ if error_video is not None:
+ if product_type in ('VLIVE_PLUS', 'VLIVE+'):
+ self.raise_login_required('This video is only available with V LIVE+.')
+ elif error_msg is not None:
+ raise ExtractorError('V LIVE reported the following error: %s' % error_msg)
+ else:
+ raise ExtractorError('Failed to extract video parameters.')
+ elif 'post' in url:
+ raise ExtractorError('Url does not appear to be a video post.', expected=True)
+ else:
+ raise ExtractorError('Failed to extract video parameters.')
- if status in ('LIVE_ON_AIR', 'BIG_EVENT_ON_AIR'):
- return self._live(video_id, webpage)
- elif status in ('VOD_ON_AIR', 'BIG_EVENT_INTRO'):
- return self._replay(video_id, webpage, long_video_id, key)
+ video_id = working_id if 'video' in url else str(video_params["videoSeq"])
- if status == 'LIVE_END':
- raise ExtractorError('Uploading for replay. Please wait...',
- expected=True)
- elif status == 'COMING_SOON':
- raise ExtractorError('Coming soon!', expected=True)
- elif status == 'CANCELED':
- raise ExtractorError('We are sorry, '
- 'but the live broadcast has been canceled.',
- expected=True)
- elif status == 'ONLY_APP':
- raise ExtractorError('Unsupported video type', expected=True)
+ video_type = video_params["type"]
+ if video_type in ('VOD'):
+ encoding_status = video_params["encodingStatus"]
+ if encoding_status == 'COMPLETE':
+ return self._replay(video_id, webpage, params, video_params)
+ else:
+ raise ExtractorError('VOD encoding not yet complete. Please try again later.',
+ expected=True)
+ elif video_type in ('LIVE'):
+ video_status = video_params["status"]
+ if video_status in ('RESERVED'):
+ raise ExtractorError('Coming soon!', expected=True)
+ elif video_status in ('ENDED', 'END'):
+ raise ExtractorError('Uploading for replay. Please wait...', expected=True)
+ else:
+ return self._live(video_id, webpage, params)
else:
- raise ExtractorError('Unknown status %s' % status)
+ raise ExtractorError('Unknown video type %s' % video_type)
- def _get_common_fields(self, webpage):
+ def _get_common_fields(self, webpage, params):
title = self._og_search_title(webpage)
- creator = self._html_search_regex(
- r'