]> jfr.im git - yt-dlp.git/blame - yt_dlp/extractor/egghead.py
[ie/matchtv] Fix extractor (#10190)
[yt-dlp.git] / yt_dlp / extractor / egghead.py
CommitLineData
8084951b 1from .common import InfoExtractor
dc6520aa 2from ..utils import (
514e8aef 3 determine_ext,
dc6520aa
S
4 int_or_none,
5 try_get,
6 unified_timestamp,
3052a30d 7 url_or_none,
dc6520aa 8)
8084951b
PH
9
10
2181983a 11class EggheadBaseIE(InfoExtractor):
12 def _call_api(self, path, video_id, resource, fatal=True):
13 return self._download_json(
14 'https://app.egghead.io/api/v1/' + path,
add96eb9 15 video_id, f'Downloading {resource} JSON', fatal=fatal)
2181983a 16
17
18class EggheadCourseIE(EggheadBaseIE):
8084951b
PH
19 IE_DESC = 'egghead.io course'
20 IE_NAME = 'egghead:course'
a687226b 21 _VALID_URL = r'https?://(?:app\.)?egghead\.io/(?:course|playlist)s/(?P<id>[^/?#&]+)'
ed807c18 22 _TESTS = [{
8084951b
PH
23 'url': 'https://egghead.io/courses/professor-frisby-introduces-composable-functional-javascript',
24 'playlist_count': 29,
25 'info_dict': {
ed807c18 26 'id': '432655',
8084951b
PH
27 'title': 'Professor Frisby Introduces Composable Functional JavaScript',
28 'description': 're:(?s)^This course teaches the ubiquitous.*You\'ll start composing functionality before you know it.$',
29 },
ed807c18 30 }, {
31 'url': 'https://app.egghead.io/playlists/professor-frisby-introduces-composable-functional-javascript',
32 'only_matching': True,
33 }]
8084951b
PH
34
35 def _real_extract(self, url):
36 playlist_id = self._match_id(url)
2181983a 37 series_path = 'series/' + playlist_id
38 lessons = self._call_api(
39 series_path + '/lessons', playlist_id, 'course lessons')
514e8aef
S
40
41 entries = []
42 for lesson in lessons:
3052a30d
S
43 lesson_url = url_or_none(lesson.get('http_url'))
44 if not lesson_url:
514e8aef
S
45 continue
46 lesson_id = lesson.get('id')
47 if lesson_id:
add96eb9 48 lesson_id = str(lesson_id)
514e8aef
S
49 entries.append(self.url_result(
50 lesson_url, ie=EggheadLessonIE.ie_key(), video_id=lesson_id))
51
2181983a 52 course = self._call_api(
53 series_path, playlist_id, 'course', False) or {}
8084951b 54
514e8aef
S
55 playlist_id = course.get('id')
56 if playlist_id:
add96eb9 57 playlist_id = str(playlist_id)
485cb375
S
58
59 return self.playlist_result(
60 entries, playlist_id, course.get('title'),
61 course.get('description'))
dc6520aa
S
62
63
2181983a 64class EggheadLessonIE(EggheadBaseIE):
dc6520aa
S
65 IE_DESC = 'egghead.io lesson'
66 IE_NAME = 'egghead:lesson'
a687226b 67 _VALID_URL = r'https?://(?:app\.)?egghead\.io/(?:api/v1/)?lessons/(?P<id>[^/?#&]+)'
514e8aef 68 _TESTS = [{
dc6520aa
S
69 'url': 'https://egghead.io/lessons/javascript-linear-data-flow-with-container-style-types-box',
70 'info_dict': {
514e8aef
S
71 'id': '1196',
72 'display_id': 'javascript-linear-data-flow-with-container-style-types-box',
dc6520aa
S
73 'ext': 'mp4',
74 'title': 'Create linear data flow with container style types (Box)',
75 'description': 'md5:9aa2cdb6f9878ed4c39ec09e85a8150e',
76 'thumbnail': r're:^https?:.*\.jpg$',
77 'timestamp': 1481296768,
78 'upload_date': '20161209',
79 'duration': 304,
80 'view_count': 0,
2181983a 81 'tags': 'count:2',
dc6520aa
S
82 },
83 'params': {
84 'skip_download': True,
85 },
514e8aef
S
86 }, {
87 'url': 'https://egghead.io/api/v1/lessons/react-add-redux-to-a-react-application',
88 'only_matching': True,
ed807c18 89 }, {
90 'url': 'https://app.egghead.io/lessons/javascript-linear-data-flow-with-container-style-types-box',
91 'only_matching': True,
514e8aef 92 }]
dc6520aa
S
93
94 def _real_extract(self, url):
514e8aef 95 display_id = self._match_id(url)
dc6520aa 96
2181983a 97 lesson = self._call_api(
98 'lessons/' + display_id, display_id, 'lesson')
514e8aef 99
add96eb9 100 lesson_id = str(lesson['id'])
514e8aef
S
101 title = lesson['title']
102
103 formats = []
104 for _, format_url in lesson['media_urls'].items():
3052a30d
S
105 format_url = url_or_none(format_url)
106 if not format_url:
514e8aef
S
107 continue
108 ext = determine_ext(format_url)
109 if ext == 'm3u8':
110 formats.extend(self._extract_m3u8_formats(
177877c5 111 format_url, lesson_id, 'mp4', m3u8_id='hls', fatal=False))
514e8aef
S
112 elif ext == 'mpd':
113 formats.extend(self._extract_mpd_formats(
114 format_url, lesson_id, mpd_id='dash', fatal=False))
115 else:
116 formats.append({
117 'url': format_url,
118 })
dc6520aa
S
119
120 return {
514e8aef
S
121 'id': lesson_id,
122 'display_id': display_id,
123 'title': title,
dc6520aa
S
124 'description': lesson.get('summary'),
125 'thumbnail': lesson.get('thumb_nail'),
126 'timestamp': unified_timestamp(lesson.get('published_at')),
127 'duration': int_or_none(lesson.get('duration')),
128 'view_count': int_or_none(lesson.get('plays_count')),
129 'tags': try_get(lesson, lambda x: x['tag_list'], list),
514e8aef 130 'series': try_get(
add96eb9 131 lesson, lambda x: x['series']['title'], str),
514e8aef 132 'formats': formats,
dc6520aa 133 }