We are currently solving the following issue within our repository. Here is the issue text:
--- BEGIN ISSUE ---
Egghead still broken
<!--

######################################################################
  WARNING!
  IGNORING THE FOLLOWING TEMPLATE WILL RESULT IN ISSUE CLOSED AS INCOMPLETE
######################################################################

-->


## Checklist

<!--
Carefully read and work through this check list in order to prevent the most common mistakes and misuse of youtube-dl:
- First of, make sure you are using the latest version of youtube-dl. Run `youtube-dl --version` and ensure your version is 2021.03.03. If it's not, see https://yt-dl.org/update on how to update. Issues with outdated version will be REJECTED.
- Make sure that all provided video/audio/playlist URLs (if any) are alive and playable in a browser.
- Make sure that all URLs and arguments with special characters are properly quoted or escaped as explained in http://yt-dl.org/escape.
- Search the bugtracker for similar issues: http://yt-dl.org/search-issues. DO NOT post duplicates.
- Finally, put x into all relevant boxes (like this [x])
-->

- [X] I'm reporting a broken site support
- [X] I've verified that I'm running youtube-dl version **2021.03.03**
- [X] I've checked that all provided URLs are alive and playable in a browser
- [X] I've checked that all URLs and arguments with special characters are properly quoted or escaped
- [X] I've searched the bugtracker for similar issues including closed ones


## Verbose log

<!--
Provide the complete verbose output of youtube-dl that clearly demonstrates the problem.
Add the `-v` flag to your command line you run youtube-dl with (`youtube-dl -v <your command line>`), copy the WHOLE output and insert it below. It should look similar to this:
 [debug] System config: []
 [debug] User config: []
 [debug] Command-line args: [u'-v', u'http://www.youtube.com/watch?v=BaW_jenozKcj']
 [debug] Encodings: locale cp1251, fs mbcs, out cp866, pref cp1251
 [debug] youtube-dl version 2021.03.03
 [debug] Python version 2.7.11 - Windows-2003Server-5.2.3790-SP2
 [debug] exe versions: ffmpeg N-75573-g1d0487f, ffprobe N-75573-g1d0487f, rtmpdump 2.4
 [debug] Proxy map: {}
 <more lines>
-->

```
$ youtube-dl -v "https://egghead.io/courses/write-your-first-program-with-the-rust-language"

[debug] System config: []
[debug] User config: []
[debug] Custom config: []
[debug] Command-line args: ['-v', 'https://egghead.io/courses/write-your-first-program-with-the-rust-language']
[debug] Encodings: locale UTF-8, fs utf-8, out utf-8, pref UTF-8
[debug] youtube-dl version 2021.03.03
[debug] Python version 3.8.5 (CPython) - Linux-5.4.0-66-generic-x86_64-with-glibc2.29
[debug] exe versions: ffmpeg 4.2.4, ffprobe 4.2.4
[debug] Proxy map: {}
[egghead:course] write-your-first-program-with-the-rust-language: Downloading course lessons JSON
[egghead:course] write-your-first-program-with-the-rust-language: Downloading course JSON
[download] Downloading playlist: Write Your First Program with the Rust Language
[egghead:course] playlist Write Your First Program with the Rust Language: Collected 15 video ids (downloading 15 of them)
[download] Downloading video 1 of 15
ERROR: no suitable InfoExtractor for URL https://app.egghead.io/lessons/rust-install-rust
  File "/home/user/.local/bin/youtube-dl", line 8, in <module>
    sys.exit(main())
  File "/home/user/.local/lib/python3.8/site-packages/youtube_dl/__init__.py", line 475, in main
    _real_main(argv)
  File "/home/user/.local/lib/python3.8/site-packages/youtube_dl/__init__.py", line 465, in _real_main
    retcode = ydl.download(all_urls)
  File "/home/user/.local/lib/python3.8/site-packages/youtube_dl/YoutubeDL.py", line 2055, in download
    res = self.extract_info(
  File "/home/user/.local/lib/python3.8/site-packages/youtube_dl/YoutubeDL.py", line 799, in extract_info
    return self.__extract_info(url, ie, download, extra_info, process)
  File "/home/user/.local/lib/python3.8/site-packages/youtube_dl/YoutubeDL.py", line 806, in wrapper
    return func(self, *args, **kwargs)
  File "/home/user/.local/lib/python3.8/site-packages/youtube_dl/YoutubeDL.py", line 838, in __extract_info
    return self.process_ie_result(ie_result, download, extra_info)
  File "/home/user/.local/lib/python3.8/site-packages/youtube_dl/YoutubeDL.py", line 924, in process_ie_result
    return self.__process_playlist(ie_result, download)
  File "/home/user/.local/lib/python3.8/site-packages/youtube_dl/YoutubeDL.py", line 1058, in __process_playlist
    entry_result = self.__process_iterable_entry(entry, download, extra)
  File "/home/user/.local/lib/python3.8/site-packages/youtube_dl/YoutubeDL.py", line 806, in wrapper
    return func(self, *args, **kwargs)
  File "/home/user/.local/lib/python3.8/site-packages/youtube_dl/YoutubeDL.py", line 1067, in __process_iterable_entry
    return self.process_ie_result(
  File "/home/user/.local/lib/python3.8/site-packages/youtube_dl/YoutubeDL.py", line 876, in process_ie_result
    return self.extract_info(ie_result['url'],
  File "/home/user/.local/lib/python3.8/site-packages/youtube_dl/YoutubeDL.py", line 801, in extract_info
    self.report_error('no suitable InfoExtractor for URL %s' % url)
  File "/home/user/.local/lib/python3.8/site-packages/youtube_dl/YoutubeDL.py", line 628, in report_error
    self.trouble(error_message, tb)
  File "/home/user/.local/lib/python3.8/site-packages/youtube_dl/YoutubeDL.py", line 590, in trouble
    tb_data = traceback.format_list(traceback.extract_stack())
```


## Description

<!--
Provide an explanation of your issue in an arbitrary form. Provide any additional information, suggested solution and as much context and examples as possible.
If work on your issue requires account credentials please provide them or explain how one can obtain them.
-->

https://github.com/ytdl-org/youtube-dl/pull/28038 fixed the URL, but I assume somewhere it's still not changed to the new URL.

--- END ISSUE ---

Below are some code segments, each from a relevant file. One or more of these files may contain bugs.
--- BEGIN FILES ---
Path: `youtube_dl/extractor/egghead.py`
Content:
```
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://egghead\.io/courses/(?P<id>[^/?#&]+)'
26     _TEST = {
27         'url': 'https://egghead.io/courses/professor-frisby-introduces-composable-functional-javascript',
28         'playlist_count': 29,
29         'info_dict': {
30             'id': '72',
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 
36     def _real_extract(self, url):
37         playlist_id = self._match_id(url)
38         series_path = 'series/' + playlist_id
39         lessons = self._call_api(
40             series_path + '/lessons', playlist_id, 'course lessons')
41 
42         entries = []
43         for lesson in lessons:
44             lesson_url = url_or_none(lesson.get('http_url'))
45             if not lesson_url:
46                 continue
47             lesson_id = lesson.get('id')
48             if lesson_id:
49                 lesson_id = compat_str(lesson_id)
50             entries.append(self.url_result(
51                 lesson_url, ie=EggheadLessonIE.ie_key(), video_id=lesson_id))
52 
53         course = self._call_api(
54             series_path, playlist_id, 'course', False) or {}
55 
56         playlist_id = course.get('id')
57         if playlist_id:
58             playlist_id = compat_str(playlist_id)
59 
60         return self.playlist_result(
61             entries, playlist_id, course.get('title'),
62             course.get('description'))
63 
64 
65 class EggheadLessonIE(EggheadBaseIE):
66     IE_DESC = 'egghead.io lesson'
67     IE_NAME = 'egghead:lesson'
68     _VALID_URL = r'https://egghead\.io/(?:api/v1/)?lessons/(?P<id>[^/?#&]+)'
69     _TESTS = [{
70         'url': 'https://egghead.io/lessons/javascript-linear-data-flow-with-container-style-types-box',
71         'info_dict': {
72             'id': '1196',
73             'display_id': 'javascript-linear-data-flow-with-container-style-types-box',
74             'ext': 'mp4',
75             'title': 'Create linear data flow with container style types (Box)',
76             'description': 'md5:9aa2cdb6f9878ed4c39ec09e85a8150e',
77             'thumbnail': r're:^https?:.*\.jpg$',
78             'timestamp': 1481296768,
79             'upload_date': '20161209',
80             'duration': 304,
81             'view_count': 0,
82             'tags': 'count:2',
83         },
84         'params': {
85             'skip_download': True,
86             'format': 'bestvideo',
87         },
88     }, {
89         'url': 'https://egghead.io/api/v1/lessons/react-add-redux-to-a-react-application',
90         'only_matching': True,
91     }]
92 
93     def _real_extract(self, url):
94         display_id = self._match_id(url)
95 
96         lesson = self._call_api(
97             'lessons/' + display_id, display_id, 'lesson')
98 
99         lesson_id = compat_str(lesson['id'])
100         title = lesson['title']
101 
102         formats = []
103         for _, format_url in lesson['media_urls'].items():
104             format_url = url_or_none(format_url)
105             if not format_url:
106                 continue
107             ext = determine_ext(format_url)
108             if ext == 'm3u8':
109                 formats.extend(self._extract_m3u8_formats(
110                     format_url, lesson_id, 'mp4', entry_protocol='m3u8',
111                     m3u8_id='hls', fatal=False))
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                 })
119         self._sort_formats(formats)
120 
121         return {
122             'id': lesson_id,
123             'display_id': display_id,
124             'title': title,
125             'description': lesson.get('summary'),
126             'thumbnail': lesson.get('thumb_nail'),
127             'timestamp': unified_timestamp(lesson.get('published_at')),
128             'duration': int_or_none(lesson.get('duration')),
129             'view_count': int_or_none(lesson.get('plays_count')),
130             'tags': try_get(lesson, lambda x: x['tag_list'], list),
131             'series': try_get(
132                 lesson, lambda x: x['series']['title'], compat_str),
133             'formats': formats,
134         }
135
```
--- END FILES ---

Please first localize the bug based on the issue statement, and then generate a patch according to the `git diff` format fenced by three backticks.

Here is an example:

```diff
diff --git a/examples/server_async.py b/examples/server_async.py
--- a/examples/server_async.py
+++ b/examples/server_async.py
@@ -313,4 +313,4 @@
 
 
 if __name__ == "__main__":
-    asyncio.run(run_async_server("."), debug=True)
+    asyncio.run(run_async_server(), debug=True)
diff --git a/examples/server_sync.py b/examples/server_sync.py
--- a/examples/server_sync.py
+++ b/examples/server_sync.py
@@ -313,5 +313,5 @@
 
 
 if __name__ == "__main__":
-    server = run_sync_server(".")
+    server = run_sync_server()
     server.shutdown()

```

diff --git a/youtube_dl/extractor/egghead.py b/youtube_dl/extractor/egghead.py
--- a/youtube_dl/extractor/egghead.py
+++ b/youtube_dl/extractor/egghead.py
@@ -22,16 +22,19 @@
 class EggheadCourseIE(EggheadBaseIE):
     IE_DESC = 'egghead.io course'
     IE_NAME = 'egghead:course'
-    _VALID_URL = r'https://egghead\.io/courses/(?P<id>[^/?#&]+)'
-    _TEST = {
+    _VALID_URL = r'https://(?:app\.)?egghead\.io/(?:course|playlist)s/(?P<id>[^/?#&]+)'
+    _TESTS = [{
         'url': 'https://egghead.io/courses/professor-frisby-introduces-composable-functional-javascript',
         'playlist_count': 29,
         'info_dict': {
-            'id': '72',
+            'id': '432655',
             'title': 'Professor Frisby Introduces Composable Functional JavaScript',
             'description': 're:(?s)^This course teaches the ubiquitous.*You\'ll start composing functionality before you know it.$',
         },
-    }
+    }, {
+        'url': 'https://app.egghead.io/playlists/professor-frisby-introduces-composable-functional-javascript',
+        'only_matching': True,
+    }]
 
     def _real_extract(self, url):
         playlist_id = self._match_id(url)
@@ -65,7 +68,7 @@
 class EggheadLessonIE(EggheadBaseIE):
     IE_DESC = 'egghead.io lesson'
     IE_NAME = 'egghead:lesson'
-    _VALID_URL = r'https://egghead\.io/(?:api/v1/)?lessons/(?P<id>[^/?#&]+)'
+    _VALID_URL = r'https://(?:app\.)?egghead\.io/(?:api/v1/)?lessons/(?P<id>[^/?#&]+)'
     _TESTS = [{
         'url': 'https://egghead.io/lessons/javascript-linear-data-flow-with-container-style-types-box',
         'info_dict': {
@@ -88,6 +91,9 @@
     }, {
         'url': 'https://egghead.io/api/v1/lessons/react-add-redux-to-a-react-application',
         'only_matching': True,
+    }, {
+        'url': 'https://app.egghead.io/lessons/javascript-linear-data-flow-with-container-style-types-box',
+        'only_matching': True,
     }]
 
     def _real_extract(self, url):

{"golden_diff": "diff --git a/youtube_dl/extractor/egghead.py b/youtube_dl/extractor/egghead.py\n--- a/youtube_dl/extractor/egghead.py\n+++ b/youtube_dl/extractor/egghead.py\n@@ -22,16 +22,19 @@\n class EggheadCourseIE(EggheadBaseIE):\n     IE_DESC = 'egghead.io course'\n     IE_NAME = 'egghead:course'\n-    _VALID_URL = r'https://egghead\\.io/courses/(?P<id>[^/?#&]+)'\n-    _TEST = {\n+    _VALID_URL = r'https://(?:app\\.)?egghead\\.io/(?:course|playlist)s/(?P<id>[^/?#&]+)'\n+    _TESTS = [{\n         'url': 'https://egghead.io/courses/professor-frisby-introduces-composable-functional-javascript',\n         'playlist_count': 29,\n         'info_dict': {\n-            'id': '72',\n+            'id': '432655',\n             'title': 'Professor Frisby Introduces Composable Functional JavaScript',\n             'description': 're:(?s)^This course teaches the ubiquitous.*You\\'ll start composing functionality before you know it.$',\n         },\n-    }\n+    }, {\n+        'url': 'https://app.egghead.io/playlists/professor-frisby-introduces-composable-functional-javascript',\n+        'only_matching': True,\n+    }]\n \n     def _real_extract(self, url):\n         playlist_id = self._match_id(url)\n@@ -65,7 +68,7 @@\n class EggheadLessonIE(EggheadBaseIE):\n     IE_DESC = 'egghead.io lesson'\n     IE_NAME = 'egghead:lesson'\n-    _VALID_URL = r'https://egghead\\.io/(?:api/v1/)?lessons/(?P<id>[^/?#&]+)'\n+    _VALID_URL = r'https://(?:app\\.)?egghead\\.io/(?:api/v1/)?lessons/(?P<id>[^/?#&]+)'\n     _TESTS = [{\n         'url': 'https://egghead.io/lessons/javascript-linear-data-flow-with-container-style-types-box',\n         'info_dict': {\n@@ -88,6 +91,9 @@\n     }, {\n         'url': 'https://egghead.io/api/v1/lessons/react-add-redux-to-a-react-application',\n         'only_matching': True,\n+    }, {\n+        'url': 'https://app.egghead.io/lessons/javascript-linear-data-flow-with-container-style-types-box',\n+        'only_matching': True,\n     }]\n \n     def _real_extract(self, url):\n", "issue": "Egghead still broken\n<!--\r\n\r\n######################################################################\r\n  WARNING!\r\n  IGNORING THE FOLLOWING TEMPLATE WILL RESULT IN ISSUE CLOSED AS INCOMPLETE\r\n######################################################################\r\n\r\n-->\r\n\r\n\r\n## Checklist\r\n\r\n<!--\r\nCarefully read and work through this check list in order to prevent the most common mistakes and misuse of youtube-dl:\r\n- First of, make sure you are using the latest version of youtube-dl. Run `youtube-dl --version` and ensure your version is 2021.03.03. If it's not, see https://yt-dl.org/update on how to update. Issues with outdated version will be REJECTED.\r\n- Make sure that all provided video/audio/playlist URLs (if any) are alive and playable in a browser.\r\n- Make sure that all URLs and arguments with special characters are properly quoted or escaped as explained in http://yt-dl.org/escape.\r\n- Search the bugtracker for similar issues: http://yt-dl.org/search-issues. DO NOT post duplicates.\r\n- Finally, put x into all relevant boxes (like this [x])\r\n-->\r\n\r\n- [X] I'm reporting a broken site support\r\n- [X] I've verified that I'm running youtube-dl version **2021.03.03**\r\n- [X] I've checked that all provided URLs are alive and playable in a browser\r\n- [X] I've checked that all URLs and arguments with special characters are properly quoted or escaped\r\n- [X] I've searched the bugtracker for similar issues including closed ones\r\n\r\n\r\n## Verbose log\r\n\r\n<!--\r\nProvide the complete verbose output of youtube-dl that clearly demonstrates the problem.\r\nAdd the `-v` flag to your command line you run youtube-dl with (`youtube-dl -v <your command line>`), copy the WHOLE output and insert it below. It should look similar to this:\r\n [debug] System config: []\r\n [debug] User config: []\r\n [debug] Command-line args: [u'-v', u'http://www.youtube.com/watch?v=BaW_jenozKcj']\r\n [debug] Encodings: locale cp1251, fs mbcs, out cp866, pref cp1251\r\n [debug] youtube-dl version 2021.03.03\r\n [debug] Python version 2.7.11 - Windows-2003Server-5.2.3790-SP2\r\n [debug] exe versions: ffmpeg N-75573-g1d0487f, ffprobe N-75573-g1d0487f, rtmpdump 2.4\r\n [debug] Proxy map: {}\r\n <more lines>\r\n-->\r\n\r\n```\r\n$ youtube-dl -v \"https://egghead.io/courses/write-your-first-program-with-the-rust-language\"\r\n\r\n[debug] System config: []\r\n[debug] User config: []\r\n[debug] Custom config: []\r\n[debug] Command-line args: ['-v', 'https://egghead.io/courses/write-your-first-program-with-the-rust-language']\r\n[debug] Encodings: locale UTF-8, fs utf-8, out utf-8, pref UTF-8\r\n[debug] youtube-dl version 2021.03.03\r\n[debug] Python version 3.8.5 (CPython) - Linux-5.4.0-66-generic-x86_64-with-glibc2.29\r\n[debug] exe versions: ffmpeg 4.2.4, ffprobe 4.2.4\r\n[debug] Proxy map: {}\r\n[egghead:course] write-your-first-program-with-the-rust-language: Downloading course lessons JSON\r\n[egghead:course] write-your-first-program-with-the-rust-language: Downloading course JSON\r\n[download] Downloading playlist: Write Your First Program with the Rust Language\r\n[egghead:course] playlist Write Your First Program with the Rust Language: Collected 15 video ids (downloading 15 of them)\r\n[download] Downloading video 1 of 15\r\nERROR: no suitable InfoExtractor for URL https://app.egghead.io/lessons/rust-install-rust\r\n  File \"/home/user/.local/bin/youtube-dl\", line 8, in <module>\r\n    sys.exit(main())\r\n  File \"/home/user/.local/lib/python3.8/site-packages/youtube_dl/__init__.py\", line 475, in main\r\n    _real_main(argv)\r\n  File \"/home/user/.local/lib/python3.8/site-packages/youtube_dl/__init__.py\", line 465, in _real_main\r\n    retcode = ydl.download(all_urls)\r\n  File \"/home/user/.local/lib/python3.8/site-packages/youtube_dl/YoutubeDL.py\", line 2055, in download\r\n    res = self.extract_info(\r\n  File \"/home/user/.local/lib/python3.8/site-packages/youtube_dl/YoutubeDL.py\", line 799, in extract_info\r\n    return self.__extract_info(url, ie, download, extra_info, process)\r\n  File \"/home/user/.local/lib/python3.8/site-packages/youtube_dl/YoutubeDL.py\", line 806, in wrapper\r\n    return func(self, *args, **kwargs)\r\n  File \"/home/user/.local/lib/python3.8/site-packages/youtube_dl/YoutubeDL.py\", line 838, in __extract_info\r\n    return self.process_ie_result(ie_result, download, extra_info)\r\n  File \"/home/user/.local/lib/python3.8/site-packages/youtube_dl/YoutubeDL.py\", line 924, in process_ie_result\r\n    return self.__process_playlist(ie_result, download)\r\n  File \"/home/user/.local/lib/python3.8/site-packages/youtube_dl/YoutubeDL.py\", line 1058, in __process_playlist\r\n    entry_result = self.__process_iterable_entry(entry, download, extra)\r\n  File \"/home/user/.local/lib/python3.8/site-packages/youtube_dl/YoutubeDL.py\", line 806, in wrapper\r\n    return func(self, *args, **kwargs)\r\n  File \"/home/user/.local/lib/python3.8/site-packages/youtube_dl/YoutubeDL.py\", line 1067, in __process_iterable_entry\r\n    return self.process_ie_result(\r\n  File \"/home/user/.local/lib/python3.8/site-packages/youtube_dl/YoutubeDL.py\", line 876, in process_ie_result\r\n    return self.extract_info(ie_result['url'],\r\n  File \"/home/user/.local/lib/python3.8/site-packages/youtube_dl/YoutubeDL.py\", line 801, in extract_info\r\n    self.report_error('no suitable InfoExtractor for URL %s' % url)\r\n  File \"/home/user/.local/lib/python3.8/site-packages/youtube_dl/YoutubeDL.py\", line 628, in report_error\r\n    self.trouble(error_message, tb)\r\n  File \"/home/user/.local/lib/python3.8/site-packages/youtube_dl/YoutubeDL.py\", line 590, in trouble\r\n    tb_data = traceback.format_list(traceback.extract_stack())\r\n```\r\n\r\n\r\n## Description\r\n\r\n<!--\r\nProvide an explanation of your issue in an arbitrary form. Provide any additional information, suggested solution and as much context and examples as possible.\r\nIf work on your issue requires account credentials please provide them or explain how one can obtain them.\r\n-->\r\n\r\nhttps://github.com/ytdl-org/youtube-dl/pull/28038 fixed the URL, but I assume somewhere it's still not changed to the new URL.\n", "before_files": [{"content": "# coding: utf-8\nfrom __future__ import unicode_literals\n\nfrom .common import InfoExtractor\nfrom ..compat import compat_str\nfrom ..utils import (\n    determine_ext,\n    int_or_none,\n    try_get,\n    unified_timestamp,\n    url_or_none,\n)\n\n\nclass EggheadBaseIE(InfoExtractor):\n    def _call_api(self, path, video_id, resource, fatal=True):\n        return self._download_json(\n            'https://app.egghead.io/api/v1/' + path,\n            video_id, 'Downloading %s JSON' % resource, fatal=fatal)\n\n\nclass EggheadCourseIE(EggheadBaseIE):\n    IE_DESC = 'egghead.io course'\n    IE_NAME = 'egghead:course'\n    _VALID_URL = r'https://egghead\\.io/courses/(?P<id>[^/?#&]+)'\n    _TEST = {\n        'url': 'https://egghead.io/courses/professor-frisby-introduces-composable-functional-javascript',\n        'playlist_count': 29,\n        'info_dict': {\n            'id': '72',\n            'title': 'Professor Frisby Introduces Composable Functional JavaScript',\n            'description': 're:(?s)^This course teaches the ubiquitous.*You\\'ll start composing functionality before you know it.$',\n        },\n    }\n\n    def _real_extract(self, url):\n        playlist_id = self._match_id(url)\n        series_path = 'series/' + playlist_id\n        lessons = self._call_api(\n            series_path + '/lessons', playlist_id, 'course lessons')\n\n        entries = []\n        for lesson in lessons:\n            lesson_url = url_or_none(lesson.get('http_url'))\n            if not lesson_url:\n                continue\n            lesson_id = lesson.get('id')\n            if lesson_id:\n                lesson_id = compat_str(lesson_id)\n            entries.append(self.url_result(\n                lesson_url, ie=EggheadLessonIE.ie_key(), video_id=lesson_id))\n\n        course = self._call_api(\n            series_path, playlist_id, 'course', False) or {}\n\n        playlist_id = course.get('id')\n        if playlist_id:\n            playlist_id = compat_str(playlist_id)\n\n        return self.playlist_result(\n            entries, playlist_id, course.get('title'),\n            course.get('description'))\n\n\nclass EggheadLessonIE(EggheadBaseIE):\n    IE_DESC = 'egghead.io lesson'\n    IE_NAME = 'egghead:lesson'\n    _VALID_URL = r'https://egghead\\.io/(?:api/v1/)?lessons/(?P<id>[^/?#&]+)'\n    _TESTS = [{\n        'url': 'https://egghead.io/lessons/javascript-linear-data-flow-with-container-style-types-box',\n        'info_dict': {\n            'id': '1196',\n            'display_id': 'javascript-linear-data-flow-with-container-style-types-box',\n            'ext': 'mp4',\n            'title': 'Create linear data flow with container style types (Box)',\n            'description': 'md5:9aa2cdb6f9878ed4c39ec09e85a8150e',\n            'thumbnail': r're:^https?:.*\\.jpg$',\n            'timestamp': 1481296768,\n            'upload_date': '20161209',\n            'duration': 304,\n            'view_count': 0,\n            'tags': 'count:2',\n        },\n        'params': {\n            'skip_download': True,\n            'format': 'bestvideo',\n        },\n    }, {\n        'url': 'https://egghead.io/api/v1/lessons/react-add-redux-to-a-react-application',\n        'only_matching': True,\n    }]\n\n    def _real_extract(self, url):\n        display_id = self._match_id(url)\n\n        lesson = self._call_api(\n            'lessons/' + display_id, display_id, 'lesson')\n\n        lesson_id = compat_str(lesson['id'])\n        title = lesson['title']\n\n        formats = []\n        for _, format_url in lesson['media_urls'].items():\n            format_url = url_or_none(format_url)\n            if not format_url:\n                continue\n            ext = determine_ext(format_url)\n            if ext == 'm3u8':\n                formats.extend(self._extract_m3u8_formats(\n                    format_url, lesson_id, 'mp4', entry_protocol='m3u8',\n                    m3u8_id='hls', fatal=False))\n            elif ext == 'mpd':\n                formats.extend(self._extract_mpd_formats(\n                    format_url, lesson_id, mpd_id='dash', fatal=False))\n            else:\n                formats.append({\n                    'url': format_url,\n                })\n        self._sort_formats(formats)\n\n        return {\n            'id': lesson_id,\n            'display_id': display_id,\n            'title': title,\n            'description': lesson.get('summary'),\n            'thumbnail': lesson.get('thumb_nail'),\n            'timestamp': unified_timestamp(lesson.get('published_at')),\n            'duration': int_or_none(lesson.get('duration')),\n            'view_count': int_or_none(lesson.get('plays_count')),\n            'tags': try_get(lesson, lambda x: x['tag_list'], list),\n            'series': try_get(\n                lesson, lambda x: x['series']['title'], compat_str),\n            'formats': formats,\n        }\n", "path": "youtube_dl/extractor/egghead.py"}], "after_files": [{"content": "# coding: utf-8\nfrom __future__ import unicode_literals\n\nfrom .common import InfoExtractor\nfrom ..compat import compat_str\nfrom ..utils import (\n    determine_ext,\n    int_or_none,\n    try_get,\n    unified_timestamp,\n    url_or_none,\n)\n\n\nclass EggheadBaseIE(InfoExtractor):\n    def _call_api(self, path, video_id, resource, fatal=True):\n        return self._download_json(\n            'https://app.egghead.io/api/v1/' + path,\n            video_id, 'Downloading %s JSON' % resource, fatal=fatal)\n\n\nclass EggheadCourseIE(EggheadBaseIE):\n    IE_DESC = 'egghead.io course'\n    IE_NAME = 'egghead:course'\n    _VALID_URL = r'https://(?:app\\.)?egghead\\.io/(?:course|playlist)s/(?P<id>[^/?#&]+)'\n    _TESTS = [{\n        'url': 'https://egghead.io/courses/professor-frisby-introduces-composable-functional-javascript',\n        'playlist_count': 29,\n        'info_dict': {\n            'id': '432655',\n            'title': 'Professor Frisby Introduces Composable Functional JavaScript',\n            'description': 're:(?s)^This course teaches the ubiquitous.*You\\'ll start composing functionality before you know it.$',\n        },\n    }, {\n        'url': 'https://app.egghead.io/playlists/professor-frisby-introduces-composable-functional-javascript',\n        'only_matching': True,\n    }]\n\n    def _real_extract(self, url):\n        playlist_id = self._match_id(url)\n        series_path = 'series/' + playlist_id\n        lessons = self._call_api(\n            series_path + '/lessons', playlist_id, 'course lessons')\n\n        entries = []\n        for lesson in lessons:\n            lesson_url = url_or_none(lesson.get('http_url'))\n            if not lesson_url:\n                continue\n            lesson_id = lesson.get('id')\n            if lesson_id:\n                lesson_id = compat_str(lesson_id)\n            entries.append(self.url_result(\n                lesson_url, ie=EggheadLessonIE.ie_key(), video_id=lesson_id))\n\n        course = self._call_api(\n            series_path, playlist_id, 'course', False) or {}\n\n        playlist_id = course.get('id')\n        if playlist_id:\n            playlist_id = compat_str(playlist_id)\n\n        return self.playlist_result(\n            entries, playlist_id, course.get('title'),\n            course.get('description'))\n\n\nclass EggheadLessonIE(EggheadBaseIE):\n    IE_DESC = 'egghead.io lesson'\n    IE_NAME = 'egghead:lesson'\n    _VALID_URL = r'https://(?:app\\.)?egghead\\.io/(?:api/v1/)?lessons/(?P<id>[^/?#&]+)'\n    _TESTS = [{\n        'url': 'https://egghead.io/lessons/javascript-linear-data-flow-with-container-style-types-box',\n        'info_dict': {\n            'id': '1196',\n            'display_id': 'javascript-linear-data-flow-with-container-style-types-box',\n            'ext': 'mp4',\n            'title': 'Create linear data flow with container style types (Box)',\n            'description': 'md5:9aa2cdb6f9878ed4c39ec09e85a8150e',\n            'thumbnail': r're:^https?:.*\\.jpg$',\n            'timestamp': 1481296768,\n            'upload_date': '20161209',\n            'duration': 304,\n            'view_count': 0,\n            'tags': 'count:2',\n        },\n        'params': {\n            'skip_download': True,\n            'format': 'bestvideo',\n        },\n    }, {\n        'url': 'https://egghead.io/api/v1/lessons/react-add-redux-to-a-react-application',\n        'only_matching': True,\n    }, {\n        'url': 'https://app.egghead.io/lessons/javascript-linear-data-flow-with-container-style-types-box',\n        'only_matching': True,\n    }]\n\n    def _real_extract(self, url):\n        display_id = self._match_id(url)\n\n        lesson = self._call_api(\n            'lessons/' + display_id, display_id, 'lesson')\n\n        lesson_id = compat_str(lesson['id'])\n        title = lesson['title']\n\n        formats = []\n        for _, format_url in lesson['media_urls'].items():\n            format_url = url_or_none(format_url)\n            if not format_url:\n                continue\n            ext = determine_ext(format_url)\n            if ext == 'm3u8':\n                formats.extend(self._extract_m3u8_formats(\n                    format_url, lesson_id, 'mp4', entry_protocol='m3u8',\n                    m3u8_id='hls', fatal=False))\n            elif ext == 'mpd':\n                formats.extend(self._extract_mpd_formats(\n                    format_url, lesson_id, mpd_id='dash', fatal=False))\n            else:\n                formats.append({\n                    'url': format_url,\n                })\n        self._sort_formats(formats)\n\n        return {\n            'id': lesson_id,\n            'display_id': display_id,\n            'title': title,\n            'description': lesson.get('summary'),\n            'thumbnail': lesson.get('thumb_nail'),\n            'timestamp': unified_timestamp(lesson.get('published_at')),\n            'duration': int_or_none(lesson.get('duration')),\n            'view_count': int_or_none(lesson.get('plays_count')),\n            'tags': try_get(lesson, lambda x: x['tag_list'], list),\n            'series': try_get(\n                lesson, lambda x: x['series']['title'], compat_str),\n            'formats': formats,\n        }\n", "path": "youtube_dl/extractor/egghead.py"}]}