logo

youtube-dl

[mirror] Download/Watch videos from video hostersgit clone https://hacktivis.me/git/mirror/youtube-dl.git

egghead.py (5107B)


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