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

Code before:
    def _entries(self, base_url, playlist_id, session_list_id):
        session_list_info = self._call_api(
            base_url, f'/Api/SessionLists/{session_list_id}?collections[0].maxCount=500&collections[0].name=ViewableItemsOnly', playlist_id)
        items = session_list_info['Items']
        if len(items) == 500:
            self.report_warning(
                'There are 500 items in this playlist. There may be more but we are unable to get them' + bug_reports_message(), only_once=True)

        for item in items:
            if item.get('TypeName') != 'Session':
                self.report_warning('Got an item in the playlist that is not a Session' + bug_reports_message(), only_once=True)
                continue
            yield {
                '_type': 'url',
                'id': item.get('Id'),
                'url': item.get('ViewerUri'),
                'title': item.get('Name'),
                'description': item.get('Description'),
                'duration': item.get('Duration'),
                'channel': traverse_obj(item, ('Parent', 'Name')),
                'channel_id': traverse_obj(item, ('Parent', 'Id'))
            }

    def _real_extract(self, url):
        mobj = self._match_valid_url(url)
        base_url, playlist_id = mobj.group('base_url', 'id')

        video_id = get_first(parse_qs(url), 'id')
        if video_id:
            if self.get_param('noplaylist'):
                self.to_screen('Downloading just video %s because of --no-playlist' % video_id)
                return self.url_result(base_url + f'/Pages/Viewer.aspx?id={video_id}', ie_key=PanoptoIE.ie_key(), video_id=video_id)
            else:
                self.to_screen(f'Downloading playlist {playlist_id}; add --no-playlist to just download video {video_id}')

        playlist_info = self._call_api(base_url, f'/Api/Playlists/{playlist_id}', playlist_id)
        return self.playlist_result(
            self._entries(base_url, playlist_id, playlist_info['SessionListId']),
            playlist_id=playlist_id, playlist_title=playlist_info.get('Name'),
            playlist_description=playlist_info.get('Description'))


class PanoptoListIE(PanoptoBaseIE):
    _VALID_URL = PanoptoBaseIE.BASE_URL_RE + r'/Pages/Sessions/List\.aspx'
    _PAGE_SIZE = 250
    _TESTS = [
        {
            'url': 'https://demo.hosted.panopto.com/Panopto/Pages/Sessions/List.aspx#folderID=%22e4c6a2fc-1214-4ca0-8fb7-aef2e29ff63a%22',
            'info_dict': {
                'id': 'e4c6a2fc-1214-4ca0-8fb7-aef2e29ff63a',
                'title': 'Showcase Videos'

Review comment (suggestion):
```suggestion
        base_url, playlist_id = self._match_valid_url(url).group('base_url', 'id')
```

Code after:
    _VALID_URL = PanoptoBaseIE.BASE_URL_RE + r'/Pages/(Viewer|Embed)\.aspx.*(?:\?|&)pid=(?P<id>[a-f0-9-]+)'
    _TESTS = [
        {
            'url': 'https://howtovideos.hosted.panopto.com/Panopto/Pages/Viewer.aspx?pid=f3b39fcf-882f-4849-93d6-a9f401236d36&id=5fa74e93-3d87-4694-b60e-aaa4012214ed&advance=true',
            'info_dict': {
                'title': 'Featured Video Tutorials',
                'id': 'f3b39fcf-882f-4849-93d6-a9f401236d36',
                'description': '',
            },
            'playlist_mincount': 36
        },
        {
            'url': 'https://utsa.hosted.panopto.com/Panopto/Pages/Viewer.aspx?pid=e2900555-3ad4-4bdb-854d-ad2401686190',
            'info_dict': {
                'title': 'Library Website Introduction Playlist',
                'id': 'e2900555-3ad4-4bdb-854d-ad2401686190',
                'description': 'md5:f958bca50a1cbda15fdc1e20d32b3ecb',
            },
            'playlist_mincount': 4
        },

    ]

    def _entries(self, base_url, playlist_id, session_list_id):
        session_list_info = self._call_api(
            base_url, f'/Api/SessionLists/{session_list_id}?collections[0].maxCount=500&collections[0].name=items', playlist_id)

        items = session_list_info['Items']
        for item in items:
            if item.get('TypeName') != 'Session':
                self.report_warning('Got an item in the playlist that is not a Session' + bug_reports_message(), only_once=True)
                continue
            yield {
                '_type': 'url',
                'id': item.get('Id'),
                'url': item.get('ViewerUri'),
                'title': item.get('Name'),
                'description': item.get('Description'),
                'duration': item.get('Duration'),
                'channel': traverse_obj(item, ('Parent', 'Name')),
                'channel_id': traverse_obj(item, ('Parent', 'Id'))
            }

    def _real_extract(self, url):
        base_url, playlist_id = self._match_valid_url(url).group('base_url', 'id')

        video_id = get_first(parse_qs(url), 'id')
        if video_id:
            if self.get_param('noplaylist'):
                self.to_screen('Downloading just video %s because of --no-playlist' % video_id)
                return self.url_result(base_url + f'/Pages/Viewer.aspx?id={video_id}', ie_key=PanoptoIE.ie_key(), video_id=video_id)