|
|
@ -20,54 +20,64 @@ class Channel9IE(InfoExtractor):
|
|
|
|
'''
|
|
|
|
'''
|
|
|
|
IE_DESC = 'Channel 9'
|
|
|
|
IE_DESC = 'Channel 9'
|
|
|
|
IE_NAME = 'channel9'
|
|
|
|
IE_NAME = 'channel9'
|
|
|
|
_VALID_URL = r'https?://(?:www\.)?channel9\.msdn\.com/(?P<contentpath>.+)/?'
|
|
|
|
_VALID_URL = r'https?://(?:www\.)?channel9\.msdn\.com/(?P<contentpath>.+?)(?P<rss>/RSS)?/?(?:[?#&]|$)'
|
|
|
|
|
|
|
|
|
|
|
|
_TESTS = [
|
|
|
|
_TESTS = [{
|
|
|
|
{
|
|
|
|
'url': 'http://channel9.msdn.com/Events/TechEd/Australia/2013/KOS002',
|
|
|
|
'url': 'http://channel9.msdn.com/Events/TechEd/Australia/2013/KOS002',
|
|
|
|
'md5': 'bbd75296ba47916b754e73c3a4bbdf10',
|
|
|
|
'md5': 'bbd75296ba47916b754e73c3a4bbdf10',
|
|
|
|
'info_dict': {
|
|
|
|
'info_dict': {
|
|
|
|
'id': 'Events/TechEd/Australia/2013/KOS002',
|
|
|
|
'id': 'Events/TechEd/Australia/2013/KOS002',
|
|
|
|
'ext': 'mp4',
|
|
|
|
'ext': 'mp4',
|
|
|
|
'title': 'Developer Kick-Off Session: Stuff We Love',
|
|
|
|
'title': 'Developer Kick-Off Session: Stuff We Love',
|
|
|
|
'description': 'md5:c08d72240b7c87fcecafe2692f80e35f',
|
|
|
|
'description': 'md5:c08d72240b7c87fcecafe2692f80e35f',
|
|
|
|
'duration': 4576,
|
|
|
|
'duration': 4576,
|
|
|
|
'thumbnail': 're:http://.*\.jpg',
|
|
|
|
'thumbnail': 're:http://.*\.jpg',
|
|
|
|
'session_code': 'KOS002',
|
|
|
|
'session_code': 'KOS002',
|
|
|
|
'session_day': 'Day 1',
|
|
|
|
'session_day': 'Day 1',
|
|
|
|
'session_room': 'Arena 1A',
|
|
|
|
'session_room': 'Arena 1A',
|
|
|
|
'session_speakers': ['Ed Blankenship', 'Andrew Coates', 'Brady Gaster', 'Patrick Klug',
|
|
|
|
'session_speakers': ['Ed Blankenship', 'Andrew Coates', 'Brady Gaster', 'Patrick Klug', 'Mads Kristensen'],
|
|
|
|
'Mads Kristensen'],
|
|
|
|
},
|
|
|
|
|
|
|
|
},
|
|
|
|
},
|
|
|
|
{
|
|
|
|
}, {
|
|
|
|
'url': 'http://channel9.msdn.com/posts/Self-service-BI-with-Power-BI-nuclear-testing',
|
|
|
|
'url': 'http://channel9.msdn.com/posts/Self-service-BI-with-Power-BI-nuclear-testing',
|
|
|
|
'md5': 'b43ee4529d111bc37ba7ee4f34813e68',
|
|
|
|
'md5': 'b43ee4529d111bc37ba7ee4f34813e68',
|
|
|
|
'info_dict': {
|
|
|
|
'info_dict': {
|
|
|
|
'id': 'posts/Self-service-BI-with-Power-BI-nuclear-testing',
|
|
|
|
'id': 'posts/Self-service-BI-with-Power-BI-nuclear-testing',
|
|
|
|
'ext': 'mp4',
|
|
|
|
'ext': 'mp4',
|
|
|
|
'title': 'Self-service BI with Power BI - nuclear testing',
|
|
|
|
'title': 'Self-service BI with Power BI - nuclear testing',
|
|
|
|
'description': 'md5:d1e6ecaafa7fb52a2cacdf9599829f5b',
|
|
|
|
'description': 'md5:d1e6ecaafa7fb52a2cacdf9599829f5b',
|
|
|
|
'duration': 1540,
|
|
|
|
'duration': 1540,
|
|
|
|
'thumbnail': 're:http://.*\.jpg',
|
|
|
|
'thumbnail': 're:http://.*\.jpg',
|
|
|
|
'authors': ['Mike Wilmot'],
|
|
|
|
'authors': ['Mike Wilmot'],
|
|
|
|
},
|
|
|
|
|
|
|
|
},
|
|
|
|
},
|
|
|
|
{
|
|
|
|
}, {
|
|
|
|
# low quality mp4 is best
|
|
|
|
# low quality mp4 is best
|
|
|
|
'url': 'https://channel9.msdn.com/Events/CPP/CppCon-2015/Ranges-for-the-Standard-Library',
|
|
|
|
'url': 'https://channel9.msdn.com/Events/CPP/CppCon-2015/Ranges-for-the-Standard-Library',
|
|
|
|
'info_dict': {
|
|
|
|
'info_dict': {
|
|
|
|
'id': 'Events/CPP/CppCon-2015/Ranges-for-the-Standard-Library',
|
|
|
|
'id': 'Events/CPP/CppCon-2015/Ranges-for-the-Standard-Library',
|
|
|
|
'ext': 'mp4',
|
|
|
|
'ext': 'mp4',
|
|
|
|
'title': 'Ranges for the Standard Library',
|
|
|
|
'title': 'Ranges for the Standard Library',
|
|
|
|
'description': 'md5:2e6b4917677af3728c5f6d63784c4c5d',
|
|
|
|
'description': 'md5:2e6b4917677af3728c5f6d63784c4c5d',
|
|
|
|
'duration': 5646,
|
|
|
|
'duration': 5646,
|
|
|
|
'thumbnail': 're:http://.*\.jpg',
|
|
|
|
'thumbnail': 're:http://.*\.jpg',
|
|
|
|
},
|
|
|
|
},
|
|
|
|
'params': {
|
|
|
|
'params': {
|
|
|
|
'skip_download': True,
|
|
|
|
'skip_download': True,
|
|
|
|
},
|
|
|
|
},
|
|
|
|
}
|
|
|
|
}, {
|
|
|
|
]
|
|
|
|
'url': 'https://channel9.msdn.com/Niners/Splendid22/Queue/76acff796e8f411184b008028e0d492b/RSS',
|
|
|
|
|
|
|
|
'info_dict': {
|
|
|
|
|
|
|
|
'id': 'Niners/Splendid22/Queue/76acff796e8f411184b008028e0d492b',
|
|
|
|
|
|
|
|
'title': 'Channel 9',
|
|
|
|
|
|
|
|
},
|
|
|
|
|
|
|
|
'playlist_count': 2,
|
|
|
|
|
|
|
|
}, {
|
|
|
|
|
|
|
|
'url': 'https://channel9.msdn.com/Events/DEVintersection/DEVintersection-2016/RSS',
|
|
|
|
|
|
|
|
'only_matching': True,
|
|
|
|
|
|
|
|
}, {
|
|
|
|
|
|
|
|
'url': 'https://channel9.msdn.com/Events/Speakers/scott-hanselman/RSS?UrlSafeName=scott-hanselman',
|
|
|
|
|
|
|
|
'only_matching': True,
|
|
|
|
|
|
|
|
}]
|
|
|
|
|
|
|
|
|
|
|
|
_RSS_URL = 'http://channel9.msdn.com/%s/RSS'
|
|
|
|
_RSS_URL = 'http://channel9.msdn.com/%s/RSS'
|
|
|
|
|
|
|
|
|
|
|
@ -254,22 +264,30 @@ class Channel9IE(InfoExtractor):
|
|
|
|
|
|
|
|
|
|
|
|
return self.playlist_result(contents)
|
|
|
|
return self.playlist_result(contents)
|
|
|
|
|
|
|
|
|
|
|
|
def _extract_list(self, content_path):
|
|
|
|
def _extract_list(self, video_id, rss_url=None):
|
|
|
|
rss = self._download_xml(self._RSS_URL % content_path, content_path, 'Downloading RSS')
|
|
|
|
if not rss_url:
|
|
|
|
|
|
|
|
rss_url = self._RSS_URL % video_id
|
|
|
|
|
|
|
|
rss = self._download_xml(rss_url, video_id, 'Downloading RSS')
|
|
|
|
entries = [self.url_result(session_url.text, 'Channel9')
|
|
|
|
entries = [self.url_result(session_url.text, 'Channel9')
|
|
|
|
for session_url in rss.findall('./channel/item/link')]
|
|
|
|
for session_url in rss.findall('./channel/item/link')]
|
|
|
|
title_text = rss.find('./channel/title').text
|
|
|
|
title_text = rss.find('./channel/title').text
|
|
|
|
return self.playlist_result(entries, content_path, title_text)
|
|
|
|
return self.playlist_result(entries, video_id, title_text)
|
|
|
|
|
|
|
|
|
|
|
|
def _real_extract(self, url):
|
|
|
|
def _real_extract(self, url):
|
|
|
|
mobj = re.match(self._VALID_URL, url)
|
|
|
|
mobj = re.match(self._VALID_URL, url)
|
|
|
|
content_path = mobj.group('contentpath')
|
|
|
|
content_path = mobj.group('contentpath')
|
|
|
|
|
|
|
|
rss = mobj.group('rss')
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if rss:
|
|
|
|
|
|
|
|
return self._extract_list(content_path, url)
|
|
|
|
|
|
|
|
|
|
|
|
webpage = self._download_webpage(url, content_path, 'Downloading web page')
|
|
|
|
webpage = self._download_webpage(
|
|
|
|
|
|
|
|
url, content_path, 'Downloading web page')
|
|
|
|
|
|
|
|
|
|
|
|
page_type_m = re.search(r'<meta name="WT.entryid" content="(?P<pagetype>[^:]+)[^"]+"/>', webpage)
|
|
|
|
page_type = self._search_regex(
|
|
|
|
if page_type_m is not None:
|
|
|
|
r'<meta[^>]+name=(["\'])WT\.entryid\1[^>]+content=(["\'])(?P<pagetype>[^:]+).+?\2',
|
|
|
|
page_type = page_type_m.group('pagetype')
|
|
|
|
webpage, 'page type', default=None, group='pagetype')
|
|
|
|
|
|
|
|
if page_type:
|
|
|
|
if page_type == 'Entry': # Any 'item'-like page, may contain downloadable content
|
|
|
|
if page_type == 'Entry': # Any 'item'-like page, may contain downloadable content
|
|
|
|
return self._extract_entry_item(webpage, content_path)
|
|
|
|
return self._extract_entry_item(webpage, content_path)
|
|
|
|
elif page_type == 'Session': # Event session page, may contain downloadable content
|
|
|
|
elif page_type == 'Session': # Event session page, may contain downloadable content
|
|
|
@ -278,6 +296,5 @@ class Channel9IE(InfoExtractor):
|
|
|
|
return self._extract_list(content_path)
|
|
|
|
return self._extract_list(content_path)
|
|
|
|
else:
|
|
|
|
else:
|
|
|
|
raise ExtractorError('Unexpected WT.entryid %s' % page_type, expected=True)
|
|
|
|
raise ExtractorError('Unexpected WT.entryid %s' % page_type, expected=True)
|
|
|
|
|
|
|
|
|
|
|
|
else: # Assuming list
|
|
|
|
else: # Assuming list
|
|
|
|
return self._extract_list(content_path)
|
|
|
|
return self._extract_list(content_path)
|
|
|
|