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

Code before:
            'url': 'https://nebula.app/lindsayellis',
            'info_dict': {
                'id': 'lindsayellis',
                'title': 'Lindsay Ellis',
                'description': 'Enjoy these hottest of takes on Disney, Transformers, and Musicals.',
            },
            'playlist_mincount': 100,
            'params': {
                'usenetrc': True,
            },
        },
    ]

    def _generate_playlist_entries(self, collection_id, channel):
        page_nr = 1
        episodes = channel['episodes']['results']
        while episodes:
            for episode in episodes:
                yield self._build_video_info(episode)
            next_url = channel['episodes']['next']
            if next_url:
                page_nr += 1
                channel = self._call_nebula_api(next_url, collection_id, auth_type='bearer', note=f'Retrieving channel page {page_nr}')
                episodes = channel['episodes']['results']
            else:
                episodes = None

    def _real_extract(self, url):
        collection_id = self._match_id(url)
        channel_url = f'https://content.watchnebula.com/video/channels/{collection_id}/'
        channel = self._call_nebula_api(channel_url, collection_id, auth_type='bearer', note='Retrieving channel')
        channel_details = channel['details']

        return self.playlist_result(
            entries=self._generate_playlist_entries(collection_id, channel),
            playlist_id=collection_id,
            playlist_title=channel_details['title'],
            playlist_description=channel_details['description']
        )


Review comment (security):
Oh, this actually turned out much simpler than I initially thought. I didn't think to extract one page in `_real_extract` and then pass that data inside 👍 

I would write this function like this, but your implementation is fine as well. So I won't insist

```py
        episodes = channel['episodes']['results']
        for page_num in itertools.count(1):
            for episode in episodes:
                yield self._build_video_info(episode)
            next_url = channel['episodes']['next']
            if not next_url:
                break
            episodes = self._call_nebula_api(
                next_url, collection_id, auth_type='bearer',
                note=f'Retrieving channel page {page_num}')['episodes']['results']
```

Code after:
                'id': 'tom-scott-presents-money',
                'title': 'Tom Scott Presents: Money',
                'description': 'Tom Scott hosts a series all about trust, negotiation and money.',
            },
            'playlist_count': 5,
            'params': {
                'usenetrc': True,
            },
        }, {
            'url': 'https://nebula.app/lindsayellis',
            'info_dict': {
                'id': 'lindsayellis',
                'title': 'Lindsay Ellis',
                'description': 'Enjoy these hottest of takes on Disney, Transformers, and Musicals.',
            },
            'playlist_mincount': 100,
            'params': {
                'usenetrc': True,
            },
        },
    ]

    def _generate_playlist_entries(self, collection_id, channel):
        episodes = channel['episodes']['results']
        for page_num in itertools.count(2):
            for episode in episodes:
                yield self._build_video_info(episode)
            next_url = channel['episodes']['next']
            if not next_url:
                break
            channel = self._call_nebula_api(next_url, collection_id, auth_type='bearer',
                                            note=f'Retrieving channel page {page_num}')
            episodes = channel['episodes']['results']

    def _real_extract(self, url):
        collection_id = self._match_id(url)
        channel_url = f'https://content.watchnebula.com/video/channels/{collection_id}/'
        channel = self._call_nebula_api(channel_url, collection_id, auth_type='bearer', note='Retrieving channel')
        channel_details = channel['details']

        return self.playlist_result(
            entries=self._generate_playlist_entries(collection_id, channel),
            playlist_id=collection_id,
            playlist_title=channel_details['title'],
            playlist_description=channel_details['description']
        )