No known key found for this signature in database
GPG Key ID: 2C393E0F18A9236D
2 changed files with 175 additions and 0 deletions
Split View
Diff Options
@ -0,0 +1,174 @@ |
|||
from __future__ import unicode_literals |
|||
|
|||
import json |
|||
import random |
|||
import re |
|||
|
|||
from .common import InfoExtractor |
|||
from ..compat import ( |
|||
compat_b64decode, |
|||
compat_HTTPError, |
|||
compat_str, |
|||
) |
|||
from ..utils import ( |
|||
ExtractorError, |
|||
orderedSet, |
|||
unescapeHTML, |
|||
urlencode_postdata, |
|||
urljoin, |
|||
) |
|||
|
|||
|
|||
class LinuxAcademyIE(InfoExtractor): |
|||
_VALID_URL = r'''(?x) |
|||
https?:// |
|||
(?:www\.)?linuxacademy\.com/cp/ |
|||
(?: |
|||
courses/lesson/course/(?P<chapter_id>\d+)/lesson/(?P<lesson_id>\d+)| |
|||
modules/view/id/(?P<course_id>\d+) |
|||
) |
|||
''' |
|||
_TESTS = [{ |
|||
'url': 'https://linuxacademy.com/cp/courses/lesson/course/1498/lesson/2/module/154', |
|||
'info_dict': { |
|||
'id': '1498-2', |
|||
'ext': 'mp4', |
|||
'title': "Introduction to the Practitioner's Brief", |
|||
}, |
|||
'params': { |
|||
'skip_download': True, |
|||
}, |
|||
'skip': 'Requires Linux Academy account credentials', |
|||
}, { |
|||
'url': 'https://linuxacademy.com/cp/courses/lesson/course/1498/lesson/2', |
|||
'only_matching': True, |
|||
}, { |
|||
'url': 'https://linuxacademy.com/cp/modules/view/id/154', |
|||
'info_dict': { |
|||
'id': '154', |
|||
'title': 'AWS Certified Cloud Practitioner', |
|||
'description': 'md5:039db7e60e4aac9cf43630e0a75fa834', |
|||
}, |
|||
'playlist_count': 41, |
|||
'skip': 'Requires Linux Academy account credentials', |
|||
}] |
|||
|
|||
_AUTHORIZE_URL = 'https://login.linuxacademy.com/authorize' |
|||
_ORIGIN_URL = 'https://linuxacademy.com' |
|||
_CLIENT_ID = 'KaWxNn1C2Gc7n83W9OFeXltd8Utb5vvx' |
|||
_NETRC_MACHINE = 'linuxacademy' |
|||
|
|||
def _real_initialize(self): |
|||
self._login() |
|||
|
|||
def _login(self): |
|||
username, password = self._get_login_info() |
|||
if username is None: |
|||
return |
|||
|
|||
def random_string(): |
|||
return ''.join([ |
|||
random.choice('0123456789ABCDEFGHIJKLMNOPQRSTUVXYZabcdefghijklmnopqrstuvwxyz-._~') |
|||
for _ in range(32)]) |
|||
|
|||
webpage, urlh = self._download_webpage_handle( |
|||
self._AUTHORIZE_URL, None, 'Downloading authorize page', query={ |
|||
'client_id': self._CLIENT_ID, |
|||
'response_type': 'token id_token', |
|||
'redirect_uri': self._ORIGIN_URL, |
|||
'scope': 'openid email user_impersonation profile', |
|||
'audience': self._ORIGIN_URL, |
|||
'state': random_string(), |
|||
'nonce': random_string(), |
|||
}) |
|||
|
|||
login_data = self._parse_json( |
|||
self._search_regex( |
|||
r'atob\(\s*(["\'])(?P<value>(?:(?!\1).)+)\1', webpage, |
|||
'login info', group='value'), None, |
|||
transform_source=lambda x: compat_b64decode(x).decode('utf-8') |
|||
)['extraParams'] |
|||
|
|||
login_data.update({ |
|||
'client_id': self._CLIENT_ID, |
|||
'redirect_uri': self._ORIGIN_URL, |
|||
'tenant': 'lacausers', |
|||
'connection': 'Username-Password-Authentication', |
|||
'username': username, |
|||
'password': password, |
|||
'sso': 'true', |
|||
}) |
|||
|
|||
login_state_url = compat_str(urlh.geturl()) |
|||
|
|||
try: |
|||
login_page = self._download_webpage( |
|||
'https://login.linuxacademy.com/usernamepassword/login', None, |
|||
'Downloading login page', data=json.dumps(login_data).encode(), |
|||
headers={ |
|||
'Content-Type': 'application/json', |
|||
'Origin': 'https://login.linuxacademy.com', |
|||
'Referer': login_state_url, |
|||
}) |
|||
except ExtractorError as e: |
|||
if isinstance(e.cause, compat_HTTPError) and e.cause.code == 401: |
|||
error = self._parse_json(e.cause.read(), None) |
|||
message = error.get('description') or error['code'] |
|||
raise ExtractorError( |
|||
'%s said: %s' % (self.IE_NAME, message), expected=True) |
|||
raise |
|||
|
|||
callback_page, urlh = self._download_webpage_handle( |
|||
'https://login.linuxacademy.com/login/callback', None, |
|||
'Downloading callback page', |
|||
data=urlencode_postdata(self._hidden_inputs(login_page)), |
|||
headers={ |
|||
'Content-Type': 'application/x-www-form-urlencoded', |
|||
'Origin': 'https://login.linuxacademy.com', |
|||
'Referer': login_state_url, |
|||
}) |
|||
|
|||
access_token = self._search_regex( |
|||
r'access_token=([^=&]+)', compat_str(urlh.geturl()), |
|||
'access token') |
|||
|
|||
self._download_webpage( |
|||
'https://linuxacademy.com/cp/login/tokenValidateLogin/token/%s' |
|||
% access_token, None, 'Downloading token validation page') |
|||
|
|||
def _real_extract(self, url): |
|||
mobj = re.match(self._VALID_URL, url) |
|||
chapter_id, lecture_id, course_id = mobj.group('chapter_id', 'lesson_id', 'course_id') |
|||
item_id = course_id if course_id else '%s-%s' % (chapter_id, lecture_id) |
|||
|
|||
webpage = self._download_webpage(url, item_id) |
|||
|
|||
# course path |
|||
if course_id: |
|||
entries = [ |
|||
self.url_result( |
|||
urljoin(url, lesson_url), ie=LinuxAcademyIE.ie_key()) |
|||
for lesson_url in orderedSet(re.findall( |
|||
r'<a[^>]+\bhref=["\'](/cp/courses/lesson/course/\d+/lesson/\d+/module/\d+)', |
|||
webpage))] |
|||
title = unescapeHTML(self._html_search_regex( |
|||
(r'class=["\']course-title["\'][^>]*>(?P<value>[^<]+)', |
|||
r'var\s+title\s*=\s*(["\'])(?P<value>(?:(?!\1).)+)\1'), |
|||
webpage, 'title', default=None, group='value')) |
|||
description = unescapeHTML(self._html_search_regex( |
|||
r'var\s+description\s*=\s*(["\'])(?P<value>(?:(?!\1).)+)\1', |
|||
webpage, 'description', default=None, group='value')) |
|||
return self.playlist_result(entries, course_id, title, description) |
|||
|
|||
# single video path |
|||
info = self._extract_jwplayer_data( |
|||
webpage, item_id, require_title=False, m3u8_id='hls',) |
|||
title = self._search_regex( |
|||
(r'>Lecture\s*:\s*(?P<value>[^<]+)', |
|||
r'lessonName\s*=\s*(["\'])(?P<value>(?:(?!\1).)+)\1'), webpage, |
|||
'title', group='value') |
|||
info.update({ |
|||
'id': item_id, |
|||
'title': title, |
|||
}) |
|||
return info |
Write
Preview
Loading…
Cancel
Save