Repository: yt-dlp/yt-dlp
File: yt_dlp/extractor/ted.py (Python)

Code before:
            'season_number': 1
        },
        'playlist_mincount': 8,
    },
        {
        # series with season
        'url': 'https://www.ted.com/series/the_way_we_work#season_2',
        'info_dict': {
            'id': '8',
            'series': 'The Way We Work',
            'description': 'md5:59469256e533e1a48c4aa926a382234c',
            'season_number': 2
        },
        'playlist_mincount': 8,
    },
    ]

    def _extract_info(self, webpage, video_name):
        return self._parse_json(self._html_search_regex('<script[^>]+id="__NEXT_DATA__"[^>]*>(.+?)</script>', webpage, 'json'), video_name)

    def _parse_playlist(self, playlist):
        playlist_entries = []

        for entry in try_get(playlist, lambda x: x['videos']['nodes'], list):
            if entry.get('__typename') == 'Video':
                playlist_entries.append(self.url_result(entry.get('canonicalUrl'), self.ie_key()))

        return playlist_entries

    def _real_extract(self, url):
        m = re.match(self._VALID_URL, url, re.VERBOSE)
        if m.group('type').startswith('embed'):
            desktop_url = m.group('proto') + 'www' + m.group('urlmain')
            return self.url_result(desktop_url, 'TED')
        name = m.group('name')
        if m.group('type_talk'):
            return self._talk_info(url, name)
        elif m.group('type_playlist'):
            return self._playlist_videos_info(url, name)
        else:
            return self._series_videos_info(url, name, m.group('season'))

    def _playlist_videos_info(self, url, name):
        '''Returns the videos of the playlist'''
        webpage = self._download_webpage(url, name, 'Downloading playlist webpage')
        info = self._extract_info(webpage, 'json')

        playlist = try_get(info, lambda x: x['props']['pageProps']['playlist'], dict)
        playlist_id = playlist.get('id')
        playlist_entries = self._parse_playlist(playlist)


Review comment (suggestion):
```suggestion
            if entry.get('__typename') == 'Video' and entry.get('canonicalUrl'):
                playlist_entries.append(self.url_result(entry['canonicalUrl'], self.ie_key()))
```

Code after:
                f = m3u8_format.copy()
                f.update({
                    'url': bitrate_url,
                    'format_id': m3u8_format['format_id'].replace('hls', 'http'),
                    'protocol': 'http',
                })
                if f.get('acodec') == 'none':
                    del f['acodec']
                formats.append(f)

        audio_download = talk_info.get('audioDownload')
        if audio_download:
            formats.append({
                'url': audio_download,
                'format_id': 'audio',
                'vcodec': 'none',
            })

        if not formats:
            external = playerData.get('external') or {}
            service = external.get('service') or ''
            ext_url = external.get('code') if service.lower() == 'youtube' else None
            return self.url_result(ext_url or external['uri'])

        self._sort_formats(formats)

        thumbnail = playerData.get('thumb') or self._og_search_property('image', webpage)
        if thumbnail:
            # trim thumbnail resize parameters
            thumbnail = thumbnail.split('?')[0]

        return {
            'id': video_id,
            'title': talk_info.get('title') or self._og_search_title(webpage),
            'uploader': talk_info.get('presenterDisplayName'),
            'thumbnail': thumbnail,
            'description': talk_info.get('description') or self._og_search_description(webpage),
            'subtitles': subtitles,
            'formats': formats,
            'duration': talk_info.get('duration') or parse_duration(self._og_search_property('video:duration', webpage)),
            'view_count': str_to_int(talk_info.get('viewedCount')),
            'upload_date': unified_strdate(talk_info.get('publishedAt')),
            'release_date': unified_strdate(talk_info.get('recordedOn')),
            'tags': try_get(playerData, lambda x: x['targeting']['tag'].split(',')),
        }


class TedSeriesIE(TedBaseIE):
    _VALID_URL = fr'{TedBaseIE._VALID_URL_BASE.format(type=r"series")}(?:#season_(?P<season>\d+))?'
    _TESTS = [{
        'url': 'https://www.ted.com/series/small_thing_big_idea',