]>
Commit | Line | Data |
---|---|---|
74539995 S |
1 | from __future__ import unicode_literals |
2 | ||
c56ad5c9 | 3 | import json |
74539995 S |
4 | import re |
5 | ||
6 | from .common import InfoExtractor | |
5d0968f0 RA |
7 | from ..compat import ( |
8 | compat_str, | |
9 | compat_HTTPError, | |
10 | ) | |
74539995 S |
11 | from ..utils import ( |
12 | clean_html, | |
13 | ExtractorError, | |
14 | remove_end, | |
15 | strip_or_none, | |
16 | unified_timestamp, | |
17 | urljoin, | |
18 | ) | |
19 | ||
20 | ||
21 | class PacktPubBaseIE(InfoExtractor): | |
22 | _PACKT_BASE = 'https://www.packtpub.com' | |
23 | _MAPT_REST = '%s/mapt-rest' % _PACKT_BASE | |
24 | ||
25 | ||
26 | class PacktPubIE(PacktPubBaseIE): | |
27 | _VALID_URL = r'https?://(?:www\.)?packtpub\.com/mapt/video/[^/]+/(?P<course_id>\d+)/(?P<chapter_id>\d+)/(?P<id>\d+)' | |
28 | ||
29 | _TEST = { | |
30 | 'url': 'https://www.packtpub.com/mapt/video/web-development/9781787122215/20528/20530/Project+Intro', | |
31 | 'md5': '1e74bd6cfd45d7d07666f4684ef58f70', | |
32 | 'info_dict': { | |
33 | 'id': '20530', | |
34 | 'ext': 'mp4', | |
35 | 'title': 'Project Intro', | |
36 | 'thumbnail': r're:(?i)^https?://.*\.jpg', | |
37 | 'timestamp': 1490918400, | |
38 | 'upload_date': '20170331', | |
39 | }, | |
40 | } | |
5d0968f0 RA |
41 | _NETRC_MACHINE = 'packtpub' |
42 | _TOKEN = None | |
43 | ||
44 | def _real_initialize(self): | |
45 | (username, password) = self._get_login_info() | |
46 | if username is None: | |
47 | return | |
5d0968f0 RA |
48 | try: |
49 | self._TOKEN = self._download_json( | |
c56ad5c9 RA |
50 | self._MAPT_REST + '/users/tokens', None, |
51 | 'Downloading Authorization Token', data=json.dumps({ | |
52 | 'email': username, | |
53 | 'password': password, | |
54 | }).encode())['data']['access'] | |
5d0968f0 | 55 | except ExtractorError as e: |
c56ad5c9 | 56 | if isinstance(e.cause, compat_HTTPError) and e.cause.code in (400, 401, 404): |
5d0968f0 RA |
57 | message = self._parse_json(e.cause.read().decode(), None)['message'] |
58 | raise ExtractorError(message, expected=True) | |
59 | raise | |
74539995 S |
60 | |
61 | def _handle_error(self, response): | |
62 | if response.get('status') != 'success': | |
63 | raise ExtractorError( | |
64 | '% said: %s' % (self.IE_NAME, response['message']), | |
65 | expected=True) | |
66 | ||
67 | def _download_json(self, *args, **kwargs): | |
68 | response = super(PacktPubIE, self)._download_json(*args, **kwargs) | |
69 | self._handle_error(response) | |
70 | return response | |
71 | ||
72 | def _real_extract(self, url): | |
73 | mobj = re.match(self._VALID_URL, url) | |
74 | course_id, chapter_id, video_id = mobj.group( | |
75 | 'course_id', 'chapter_id', 'id') | |
76 | ||
5d0968f0 RA |
77 | headers = {} |
78 | if self._TOKEN: | |
c56ad5c9 | 79 | headers['Authorization'] = 'Bearer ' + self._TOKEN |
74539995 S |
80 | video = self._download_json( |
81 | '%s/users/me/products/%s/chapters/%s/sections/%s' | |
82 | % (self._MAPT_REST, course_id, chapter_id, video_id), video_id, | |
5d0968f0 | 83 | 'Downloading JSON video', headers=headers)['data'] |
74539995 S |
84 | |
85 | content = video.get('content') | |
86 | if not content: | |
5d0968f0 | 87 | self.raise_login_required('This video is locked') |
74539995 S |
88 | |
89 | video_url = content['file'] | |
90 | ||
91 | metadata = self._download_json( | |
92 | '%s/products/%s/chapters/%s/sections/%s/metadata' | |
93 | % (self._MAPT_REST, course_id, chapter_id, video_id), | |
94 | video_id)['data'] | |
95 | ||
96 | title = metadata['pageTitle'] | |
97 | course_title = metadata.get('title') | |
98 | if course_title: | |
99 | title = remove_end(title, ' - %s' % course_title) | |
100 | timestamp = unified_timestamp(metadata.get('publicationDate')) | |
101 | thumbnail = urljoin(self._PACKT_BASE, metadata.get('filepath')) | |
102 | ||
103 | return { | |
104 | 'id': video_id, | |
105 | 'url': video_url, | |
106 | 'title': title, | |
107 | 'thumbnail': thumbnail, | |
108 | 'timestamp': timestamp, | |
109 | } | |
110 | ||
111 | ||
112 | class PacktPubCourseIE(PacktPubBaseIE): | |
113 | _VALID_URL = r'(?P<url>https?://(?:www\.)?packtpub\.com/mapt/video/[^/]+/(?P<id>\d+))' | |
114 | _TEST = { | |
115 | 'url': 'https://www.packtpub.com/mapt/video/web-development/9781787122215', | |
116 | 'info_dict': { | |
117 | 'id': '9781787122215', | |
118 | 'title': 'Learn Nodejs by building 12 projects [Video]', | |
119 | }, | |
120 | 'playlist_count': 90, | |
121 | } | |
122 | ||
123 | @classmethod | |
124 | def suitable(cls, url): | |
125 | return False if PacktPubIE.suitable(url) else super( | |
126 | PacktPubCourseIE, cls).suitable(url) | |
127 | ||
128 | def _real_extract(self, url): | |
129 | mobj = re.match(self._VALID_URL, url) | |
130 | url, course_id = mobj.group('url', 'id') | |
131 | ||
132 | course = self._download_json( | |
133 | '%s/products/%s/metadata' % (self._MAPT_REST, course_id), | |
134 | course_id)['data'] | |
135 | ||
136 | entries = [] | |
137 | for chapter_num, chapter in enumerate(course['tableOfContents'], 1): | |
138 | if chapter.get('type') != 'chapter': | |
139 | continue | |
140 | children = chapter.get('children') | |
141 | if not isinstance(children, list): | |
142 | continue | |
143 | chapter_info = { | |
144 | 'chapter': chapter.get('title'), | |
145 | 'chapter_number': chapter_num, | |
146 | 'chapter_id': chapter.get('id'), | |
147 | } | |
148 | for section in children: | |
149 | if section.get('type') != 'section': | |
150 | continue | |
151 | section_url = section.get('seoUrl') | |
152 | if not isinstance(section_url, compat_str): | |
153 | continue | |
154 | entry = { | |
155 | '_type': 'url_transparent', | |
156 | 'url': urljoin(url + '/', section_url), | |
157 | 'title': strip_or_none(section.get('title')), | |
158 | 'description': clean_html(section.get('summary')), | |
159 | 'ie_key': PacktPubIE.ie_key(), | |
160 | } | |
161 | entry.update(chapter_info) | |
162 | entries.append(entry) | |
163 | ||
164 | return self.playlist_result(entries, course_id, course.get('title')) |