youtube-dl

Another place where youtube-dl lives on
git clone git://git.oshgnacknak.de/youtube-dl.git
Log | Files | Refs | README | LICENSE

egghead.py (5107B)


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