]>
Raphaƫl G. Git Repositories - youtubedl/blob - youtube_dl/extractor/frontendmasters.py
   2 from __future__ 
import unicode_literals
 
   6 from .common 
import InfoExtractor
 
  19 class FrontendMastersBaseIE(InfoExtractor
): 
  20     _API_BASE 
= 'https://api.frontendmasters.com/v1/kabuki' 
  21     _LOGIN_URL 
= 'https://frontendmasters.com/login/' 
  23     _NETRC_MACHINE 
= 'frontendmasters' 
  26         'low': {'width': 480, 'height': 360}, 
  27         'mid': {'width': 1280, 'height': 720}, 
  28         'high': {'width': 1920, 'height': 1080} 
  31     def _real_initialize(self
): 
  35         (username
, password
) = self
._get
_login
_info
() 
  39         login_page 
= self
._download
_webpage
( 
  40             self
._LOGIN
_URL
, None, 'Downloading login page') 
  42         login_form 
= self
._hidden
_inputs
(login_page
) 
  49         post_url 
= self
._search
_regex
( 
  50             r
'<form[^>]+action=(["\'])(?P
<url
>.+?
)\
1', login_page, 
  51             'post_url
', default=self._LOGIN_URL, group='url
') 
  53         if not post_url.startswith('http
'): 
  54             post_url = compat_urlparse.urljoin(self._LOGIN_URL, post_url) 
  56         response = self._download_webpage( 
  57             post_url, None, 'Logging 
in', data=urlencode_postdata(login_form), 
  58             headers={'Content
-Type
': 'application
/x
-www
-form
-urlencoded
'}) 
  61         if any(p in response for p in ( 
  62                 'wp
-login
.php?action
=logout
', '>Logout
')): 
  65         error = self._html_search_regex( 
  66             r'class=(["\'])(?:(?!\1).)*\bMessageAlert\b(?:(?!\1).)*\1[^>]*>(?P<error>[^<]+)<', 
  67             response, 'error message', default=None, group='error') 
  69             raise ExtractorError('Unable to login: %s' % error, expected=True) 
  70         raise ExtractorError('Unable to log in') 
  73 class FrontendMastersPageBaseIE(FrontendMastersBaseIE): 
  74     def _download_course(self, course_name, url): 
  75         return self._download_json( 
  76             '%s/courses/%s' % (self._API_BASE, course_name), course_name, 
  77             'Downloading course JSON', headers={'Referer': url}) 
  80     def _extract_chapters(course): 
  82         lesson_elements = course.get('lessonElements') 
  83         if isinstance(lesson_elements, list): 
  84             chapters = [url_or_none(e) for e in lesson_elements if url_or_none(e)] 
  88     def _extract_lesson(chapters, lesson_id, lesson): 
  89         title = lesson.get('title') or lesson_id 
  90         display_id = lesson.get('slug') 
  91         description = lesson.get('description') 
  92         thumbnail = lesson.get('thumbnail') 
  95         index = lesson.get('index') 
  96         element_index = lesson.get('elementIndex') 
  97         if (isinstance(index, int) and isinstance(element_index, int) 
  98                 and index < element_index): 
  99             chapter_number = element_index - index 
 100         chapter = (chapters[chapter_number - 1] 
 101                    if chapter_number - 1 < len(chapters) else None) 
 104         timestamp = lesson.get('timestamp') 
 105         if isinstance(timestamp, compat_str): 
 107                 r'(?P<start>\d{1,2}:\d{1,2}:\d{1,2})\s*-(?P<end>\s*\d{1,2}:\d{1,2}:\d{1,2})', 
 110                 duration = parse_duration(mobj.group('end')) - parse_duration( 
 114             '_type': 'url_transparent', 
 115             'url': 'frontendmasters:%s' % lesson_id, 
 116             'ie_key': FrontendMastersIE.ie_key(), 
 118             'display_id': display_id, 
 120             'description': description, 
 121             'thumbnail': thumbnail, 
 122             'duration': duration, 
 124             'chapter_number': chapter_number, 
 128 class FrontendMastersIE(FrontendMastersBaseIE): 
 129     _VALID_URL = r'(?:frontendmasters:|https?://api\.frontendmasters\.com/v\d+/kabuki/video/)(?P<id>[^/]+)' 
 131         'url': 'https://api.frontendmasters.com/v1/kabuki/video/a2qogef6ba', 
 132         'md5': '7f161159710d6b7016a4f4af6fcb05e2', 
 136             'title': 'a2qogef6ba', 
 138         'skip': 'Requires FrontendMasters account credentials', 
 140         'url': 'frontendmasters:a2qogef6ba', 
 141         'only_matching': True, 
 144     def _real_extract(self, url): 
 145         lesson_id = self._match_id(url) 
 147         source_url = '%s/video/%s/source' % (self._API_BASE, lesson_id) 
 150         for ext in ('webm', 'mp4'): 
 151             for quality in ('low', 'mid', 'high'): 
 152                 resolution = self._QUALITIES[quality].copy() 
 153                 format_id = '%s-%s' % (ext, quality) 
 154                 format_url = self._download_json( 
 155                     source_url, lesson_id, 
 156                     'Downloading %s source JSON' % format_id, query={ 
 158                         'r': resolution['height'], 
 161                     }, fatal=False)['url'] 
 166                 f = resolution.copy() 
 170                     'format_id': format_id, 
 173         self._sort_formats(formats) 
 177                 'url': '%s/transcripts/%s.vtt' % (self._API_BASE, lesson_id), 
 185             'subtitles': subtitles 
 189 class FrontendMastersLessonIE(FrontendMastersPageBaseIE): 
 190     _VALID_URL = r'https?://(?:www\.)?frontendmasters\.com/courses/(?P<course_name>[^/]+)/(?P<lesson_name>[^/]+)' 
 192         'url': 'https://frontendmasters.com/courses/web-development/tools', 
 195             'display_id': 'tools', 
 198             'description': 'md5:82c1ea6472e88ed5acd1829fe992e4f7', 
 199             'thumbnail': r're:^https?://.*\.jpg$', 
 200             'chapter': 'Introduction', 
 204             'skip_download': True, 
 206         'skip': 'Requires FrontendMasters account credentials', 
 209     def _real_extract(self, url): 
 210         mobj = re.match(self._VALID_URL, url) 
 211         course_name, lesson_name = mobj.group('course_name', 'lesson_name') 
 213         course = self._download_course(course_name, url) 
 215         lesson_id, lesson = next( 
 217             for video_id, data in course['lessonData'].items() 
 218             if data.get('slug') == lesson_name) 
 220         chapters = self._extract_chapters(course) 
 221         return self._extract_lesson(chapters, lesson_id, lesson) 
 224 class FrontendMastersCourseIE(FrontendMastersPageBaseIE): 
 225     _VALID_URL = r'https?://(?:www\.)?frontendmasters\.com/courses/(?P<id>[^/]+)' 
 227         'url': 'https://frontendmasters.com/courses/web-development/', 
 229             'id': 'web-development', 
 230             'title': 'Introduction to Web Development', 
 231             'description': 'md5:9317e6e842098bf725d62360e52d49a6', 
 233         'playlist_count': 81, 
 234         'skip': 'Requires FrontendMasters account credentials', 
 238     def suitable(cls, url): 
 239         return False if FrontendMastersLessonIE.suitable(url) else super( 
 240             FrontendMastersBaseIE, cls).suitable(url) 
 242     def _real_extract(self, url): 
 243         course_name = self._match_id(url) 
 245         course = self._download_course(course_name, url) 
 247         chapters = self._extract_chapters(course) 
 250             course['lessonData'].values(), key=lambda data: data['index']) 
 253         for lesson in lessons: 
 254             lesson_name = lesson.get('slug') 
 257             lesson_id = lesson.get('hash') or lesson.get('statsId') 
 258             entries.append(self._extract_lesson(chapters, lesson_id, lesson)) 
 260         title = course.get('title') 
 261         description = course.get('description') 
 263         return self.playlist_result(entries, course_name, title, description)