299 lines
12 KiB
Python
299 lines
12 KiB
Python
from __future__ import unicode_literals
|
|
|
|
import re
|
|
|
|
from .common import InfoExtractor
|
|
from ..compat import (
|
|
compat_parse_qs,
|
|
compat_urllib_parse_unquote,
|
|
)
|
|
from ..utils import (
|
|
determine_ext,
|
|
ExtractorError,
|
|
int_or_none,
|
|
urlencode_postdata,
|
|
get_element_by_attribute,
|
|
mimetype2ext,
|
|
)
|
|
|
|
|
|
class MetacafeIE(InfoExtractor):
|
|
_VALID_URL = r'https?://(?:www\.)?metacafe\.com/watch/(?P<video_id>[^/]+)/(?P<display_id>[^/?#]+)'
|
|
_DISCLAIMER = 'http://www.metacafe.com/family_filter/'
|
|
_FILTER_POST = 'http://www.metacafe.com/f/index.php?inputType=filter&controllerGroup=user'
|
|
IE_NAME = 'metacafe'
|
|
_TESTS = [
|
|
# Youtube video
|
|
{
|
|
'add_ie': ['Youtube'],
|
|
'url': 'http://metacafe.com/watch/yt-_aUehQsCQtM/the_electric_company_short_i_pbs_kids_go/',
|
|
'info_dict': {
|
|
'id': '_aUehQsCQtM',
|
|
'ext': 'mp4',
|
|
'upload_date': '20090102',
|
|
'title': 'The Electric Company | "Short I" | PBS KIDS GO!',
|
|
'description': 'md5:2439a8ef6d5a70e380c22f5ad323e5a8',
|
|
'uploader': 'PBS',
|
|
'uploader_id': 'PBS'
|
|
}
|
|
},
|
|
# Normal metacafe video
|
|
{
|
|
'url': 'http://www.metacafe.com/watch/11121940/news_stuff_you_wont_do_with_your_playstation_4/',
|
|
'md5': '6e0bca200eaad2552e6915ed6fd4d9ad',
|
|
'info_dict': {
|
|
'id': '11121940',
|
|
'ext': 'mp4',
|
|
'title': 'News: Stuff You Won\'t Do with Your PlayStation 4',
|
|
'uploader': 'ign',
|
|
'description': 'Sony released a massive FAQ on the PlayStation Blog detailing the PS4\'s capabilities and limitations.',
|
|
},
|
|
'skip': 'Page is temporarily unavailable.',
|
|
},
|
|
# metacafe video with family filter
|
|
{
|
|
'url': 'http://www.metacafe.com/watch/2155630/adult_art_by_david_hart_156/',
|
|
'md5': 'b06082c5079bbdcde677a6291fbdf376',
|
|
'info_dict': {
|
|
'id': '2155630',
|
|
'ext': 'mp4',
|
|
'title': 'Adult Art By David Hart #156',
|
|
'uploader': 'hartistry',
|
|
'description': 'Adult Art By David Hart. All the Art Works presented here are not in the possession of the American Artist, David John Hart. The paintings are in collections worldwide of individuals, countries, art museums, foundations and charities.',
|
|
}
|
|
},
|
|
# AnyClip video
|
|
{
|
|
'url': 'http://www.metacafe.com/watch/an-dVVXnuY7Jh77J/the_andromeda_strain_1971_stop_the_bomb_part_3/',
|
|
'info_dict': {
|
|
'id': 'an-dVVXnuY7Jh77J',
|
|
'ext': 'mp4',
|
|
'title': 'The Andromeda Strain (1971): Stop the Bomb Part 3',
|
|
'uploader': 'AnyClip',
|
|
'description': 'md5:cbef0460d31e3807f6feb4e7a5952e5b',
|
|
},
|
|
},
|
|
# age-restricted video
|
|
{
|
|
'url': 'http://www.metacafe.com/watch/5186653/bbc_internal_christmas_tape_79_uncensored_outtakes_etc/',
|
|
'md5': '98dde7c1a35d02178e8ab7560fe8bd09',
|
|
'info_dict': {
|
|
'id': '5186653',
|
|
'ext': 'mp4',
|
|
'title': 'BBC INTERNAL Christmas Tape \'79 - UNCENSORED Outtakes, Etc.',
|
|
'uploader': 'Dwayne Pipe',
|
|
'description': 'md5:950bf4c581e2c059911fa3ffbe377e4b',
|
|
'age_limit': 18,
|
|
},
|
|
},
|
|
# cbs video
|
|
{
|
|
'url': 'http://www.metacafe.com/watch/cb-8VD4r_Zws8VP/open_this_is_face_the_nation_february_9/',
|
|
'info_dict': {
|
|
'id': '8VD4r_Zws8VP',
|
|
'ext': 'flv',
|
|
'title': 'Open: This is Face the Nation, February 9',
|
|
'description': 'md5:8a9ceec26d1f7ed6eab610834cc1a476',
|
|
'duration': 96,
|
|
'uploader': 'CBSI-NEW',
|
|
'upload_date': '20140209',
|
|
'timestamp': 1391959800,
|
|
},
|
|
'params': {
|
|
# rtmp download
|
|
'skip_download': True,
|
|
},
|
|
},
|
|
# Movieclips.com video
|
|
{
|
|
'url': 'http://www.metacafe.com/watch/mv-Wy7ZU/my_week_with_marilyn_do_you_love_me/',
|
|
'info_dict': {
|
|
'id': 'mv-Wy7ZU',
|
|
'ext': 'mp4',
|
|
'title': 'My Week with Marilyn - Do You Love Me?',
|
|
'description': 'From the movie My Week with Marilyn - Colin (Eddie Redmayne) professes his love to Marilyn (Michelle Williams) and gets her to promise to return to set and finish the movie.',
|
|
'uploader': 'movie_trailers',
|
|
'duration': 176,
|
|
},
|
|
'params': {
|
|
'skip_download': 'requires rtmpdump',
|
|
}
|
|
}
|
|
]
|
|
|
|
def report_disclaimer(self):
|
|
self.to_screen('Retrieving disclaimer')
|
|
|
|
def _confirm_age(self):
|
|
# Retrieve disclaimer
|
|
self.report_disclaimer()
|
|
self._download_webpage(self._DISCLAIMER, None, False, 'Unable to retrieve disclaimer')
|
|
|
|
# Confirm age
|
|
self.report_age_confirmation()
|
|
self._download_webpage(
|
|
self._FILTER_POST, None, False, 'Unable to confirm age',
|
|
data=urlencode_postdata({
|
|
'filters': '0',
|
|
'submit': "Continue - I'm over 18",
|
|
}), headers={
|
|
'Content-Type': 'application/x-www-form-urlencoded',
|
|
})
|
|
|
|
def _real_extract(self, url):
|
|
# Extract id and simplified title from URL
|
|
video_id, display_id = re.match(self._VALID_URL, url).groups()
|
|
|
|
# the video may come from an external site
|
|
m_external = re.match(r'^(\w{2})-(.*)$', video_id)
|
|
if m_external is not None:
|
|
prefix, ext_id = m_external.groups()
|
|
# Check if video comes from YouTube
|
|
if prefix == 'yt':
|
|
return self.url_result('http://www.youtube.com/watch?v=%s' % ext_id, 'Youtube')
|
|
# CBS videos use theplatform.com
|
|
if prefix == 'cb':
|
|
return self.url_result('theplatform:%s' % ext_id, 'ThePlatform')
|
|
|
|
# self._confirm_age()
|
|
|
|
# AnyClip videos require the flashversion cookie so that we get the link
|
|
# to the mp4 file
|
|
headers = {}
|
|
headers['Cookie'] = 'user=%7B%22ffilter%22%3Afalse%7D;';
|
|
if video_id.startswith('an-'):
|
|
headers['Cookie'] += ' flashVersion=0;'
|
|
|
|
# Retrieve video webpage to extract further information
|
|
webpage = self._download_webpage(url, video_id, headers=headers)
|
|
|
|
error = get_element_by_attribute(
|
|
'class', 'notfound-page-title', webpage)
|
|
if error:
|
|
raise ExtractorError(error, expected=True)
|
|
|
|
video_title = self._html_search_meta(
|
|
['og:title', 'twitter:title'], webpage, 'title', default=None) or self._search_regex(r'<h1>(.*?)</h1>', webpage, 'title')
|
|
|
|
# Extract URL, uploader and title from webpage
|
|
self.report_extraction(video_id)
|
|
video_url = None
|
|
mobj = re.search(r'(?m)&(?:media|video)URL=([^&]+)', webpage)
|
|
if mobj is not None:
|
|
mediaURL = compat_urllib_parse_unquote(mobj.group(1))
|
|
video_ext = determine_ext(mediaURL)
|
|
|
|
# Extract gdaKey if available
|
|
mobj = re.search(r'(?m)&gdaKey=(.*?)&', webpage)
|
|
if mobj is None:
|
|
video_url = mediaURL
|
|
else:
|
|
gdaKey = mobj.group(1)
|
|
video_url = '%s?__gda__=%s' % (mediaURL, gdaKey)
|
|
if video_url is None:
|
|
mobj = re.search(r'<video src="([^"]+)"', webpage)
|
|
if mobj:
|
|
video_url = mobj.group(1)
|
|
video_ext = 'mp4'
|
|
if video_url is None:
|
|
flashvars = self._search_regex(
|
|
r' name="flashvars" value="(.*?)"', webpage, 'flashvars',
|
|
default=None)
|
|
if flashvars:
|
|
vardict = compat_parse_qs(flashvars)
|
|
if 'mediaData' not in vardict:
|
|
raise ExtractorError('Unable to extract media URL')
|
|
mobj = re.search(
|
|
r'"mediaURL":"(?P<mediaURL>http.*?)",(.*?)"key":"(?P<key>.*?)"', vardict['mediaData'][0])
|
|
if mobj is None:
|
|
raise ExtractorError('Unable to extract media URL')
|
|
mediaURL = mobj.group('mediaURL').replace('\\/', '/')
|
|
video_url = '%s?__gda__=%s' % (mediaURL, mobj.group('key'))
|
|
video_ext = determine_ext(video_url)
|
|
if video_url is None:
|
|
player_url = self._search_regex(
|
|
r"swfobject\.embedSWF\('([^']+)'",
|
|
webpage, 'config URL', default=None)
|
|
if player_url:
|
|
config_url = self._search_regex(
|
|
r'config=(.+)$', player_url, 'config URL')
|
|
config_doc = self._download_xml(
|
|
config_url, video_id,
|
|
note='Downloading video config')
|
|
smil_url = config_doc.find('.//properties').attrib['smil_file']
|
|
smil_doc = self._download_xml(
|
|
smil_url, video_id,
|
|
note='Downloading SMIL document')
|
|
base_url = smil_doc.find('./head/meta').attrib['base']
|
|
video_url = []
|
|
for vn in smil_doc.findall('.//video'):
|
|
br = int(vn.attrib['system-bitrate'])
|
|
play_path = vn.attrib['src']
|
|
video_url.append({
|
|
'format_id': 'smil-%d' % br,
|
|
'url': base_url,
|
|
'play_path': play_path,
|
|
'page_url': url,
|
|
'player_url': player_url,
|
|
'ext': play_path.partition(':')[0],
|
|
})
|
|
if video_url is None:
|
|
flashvars = self._parse_json(self._search_regex(
|
|
r'flashvars\s*=\s*({.*});', webpage, 'flashvars',
|
|
default=None), video_id, fatal=False)
|
|
if flashvars:
|
|
video_url = []
|
|
for source in flashvars.get('sources'):
|
|
source_url = source.get('src')
|
|
if not source_url:
|
|
continue
|
|
ext = mimetype2ext(source.get('type')) or determine_ext(source_url)
|
|
if ext == 'm3u8':
|
|
video_url.extend(self._extract_m3u8_formats(
|
|
source_url, video_id, 'mp4',
|
|
'm3u8_native', m3u8_id='hls', fatal=False))
|
|
else:
|
|
video_url.append({
|
|
'url': source_url,
|
|
'ext': ext,
|
|
})
|
|
|
|
if video_url is None:
|
|
raise ExtractorError('Unsupported video type')
|
|
|
|
description = self._html_search_meta(
|
|
['og:description', 'twitter:description', 'description'],
|
|
webpage, 'title', fatal=False)
|
|
thumbnail = self._html_search_meta(
|
|
['og:image', 'twitter:image'], webpage, 'title', fatal=False)
|
|
video_uploader = self._html_search_regex(
|
|
r'submitter=(.*?);|googletag\.pubads\(\)\.setTargeting\("(?:channel|submiter)","([^"]+)"\);',
|
|
webpage, 'uploader nickname', fatal=False)
|
|
duration = int_or_none(
|
|
self._html_search_meta('video:duration', webpage, default=None))
|
|
age_limit = (
|
|
18
|
|
if re.search(r'(?:"contentRating":|"rating",)"restricted"', webpage)
|
|
else 0)
|
|
|
|
if isinstance(video_url, list):
|
|
formats = video_url
|
|
else:
|
|
formats = [{
|
|
'url': video_url,
|
|
'ext': video_ext,
|
|
}]
|
|
self._sort_formats(formats)
|
|
|
|
return {
|
|
'id': video_id,
|
|
'display_id': display_id,
|
|
'description': description,
|
|
'uploader': video_uploader,
|
|
'title': video_title,
|
|
'thumbnail': thumbnail,
|
|
'age_limit': age_limit,
|
|
'formats': formats,
|
|
'duration': duration,
|
|
}
|