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

Code before:
            'series_id': 64,
            'season': 'Season 3',
            'season_number': 3,
            'episode': 'Episode 1',
            'episode_number': 1
        }
    }]

    def _perform_login(self, username, password):
        login_webpage = self._download_webpage('https://nubiles-porn.com/login', video_id=None)
        inputs = self._hidden_inputs(login_webpage)
        inputs.update({'username': username, 'password': password})
        self._request_webpage('https://nubiles-porn.com/authentication/login', None, data=urlencode_postdata(inputs))

    def _real_extract(self, url):
        url_match = self._match_valid_url(url)
        video_id = url_match.group('id')
        page = self._download_webpage(url, video_id)

        media_entries = self._parse_html5_media_entries(
            url, get_element_by_class('watch-page-video-wrapper', page), video_id)[0]

        channel = re.search(
            r'/video/website/(?P<id>\d+).+>(?P<name>\w+).com', get_element_html_by_class('site-link', page))
        channel_name = re.sub('([^A-Z]+)([A-Z])', r"\1 \2", channel.group('name'))
        channel_id = int_or_none(channel.group('id'))

        return {
            'id': video_id,
            'title': self._search_regex('<h2>([^<]+)</h2>', page, 'title'),
            'formats': media_entries.get('formats'),
            'display_id': url_match.group('display_id'),
            'thumbnail': media_entries.get('thumbnail'),
            'description': clean_html(get_element_html_by_class('content-pane-description', page)),
            'timestamp': unified_timestamp(get_element_by_class('date', page)),
            'channel': channel_name,
            'channel_id': channel_id,
            'channel_url': f'https://members.nubiles-porn.com/video/website/{channel_id}',
            'like_count': int_or_none(get_element_by_id('likecount', page)),
            'average_rating': float_or_none(get_element_by_class('score', page)),
            'age_limit': 18,
            'categories': list(map(clean_html, get_elements_by_class('btn', get_element_by_class('categories', page)))),
            'tags': list(map(clean_html, get_elements_by_class('btn', get_elements_by_class('tags', page)[1]))),
            'cast': get_elements_by_class('content-pane-performer', page),
            'availability': 'needs_auth',
            'series': channel_name,
            'series_id': channel_id,
            'season_number': int_or_none(url_match.group('season')),
            'episode_number': int_or_none(url_match.group('episode'))
        }


Review comment (bug):
If regex doesn't match, this shouldn't crash

```suggestion
        channel_id, channel_name = self._search_regex(
            r'/video/website/(?P<id>\d+).+>(?P<name>\w+).com', get_element_html_by_class('site-link', page),
            'channel', fatal=False, group=('id', 'name')) or (None, None)
        channel_name = re.sub(r'([^A-Z]+)([A-Z])', r'\1 \2', channel_name)
```

Code after:
                           'Glasses', 'Hardcore', 'Milf', 'Shaved Pussy', 'Tattoos', 'YoungerMommy.com'],
            'tags': list,
            'cast': ['Kenzie Love'],
            'availability': 'needs_auth',
            'series': 'Younger Mommy',
            'series_id': '64',
            'season': 'Season 3',
            'season_number': 3,
            'episode': 'Episode 1',
            'episode_number': 1
        }
    }]

    def _perform_login(self, username, password):
        login_webpage = self._download_webpage('https://nubiles-porn.com/login', video_id=None)
        inputs = self._hidden_inputs(login_webpage)
        inputs.update({'username': username, 'password': password})
        self._request_webpage('https://nubiles-porn.com/authentication/login', None, data=urlencode_postdata(inputs))

    def _real_extract(self, url):
        url_match = self._match_valid_url(url)
        video_id = url_match.group('id')
        page = self._download_webpage(url, video_id)

        media_entries = self._parse_html5_media_entries(
            url, get_element_by_class('watch-page-video-wrapper', page), video_id)[0]

        channel_id, channel_name = self._search_regex(
            r'/video/website/(?P<id>\d+).+>(?P<name>\w+).com', get_element_html_by_class('site-link', page),
            'channel', fatal=False, group=('id', 'name')) or (None, None)
        channel_name = re.sub(r'([^A-Z]+)([A-Z]+)', r'\1 \2', channel_name)

        return {
            'id': video_id,
            'title': self._search_regex('<h2>([^<]+)</h2>', page, 'title', fatal=False),
            'formats': media_entries.get('formats'),
            'display_id': url_match.group('display_id'),
            'thumbnail': media_entries.get('thumbnail'),
            'description': clean_html(get_element_html_by_class('content-pane-description', page)),
            'timestamp': unified_timestamp(get_element_by_class('date', page)),
            'channel': channel_name,
            'channel_id': channel_id,
            'channel_url': format_field(channel_id, None, 'https://members.nubiles-porn.com/video/website/%s'),
            'like_count': int_or_none(get_element_by_id('likecount', page)),
            'average_rating': float_or_none(get_element_by_class('score', page)),
            'age_limit': 18,
            'categories': try_call(lambda: list(map(clean_html, get_elements_by_class('btn', get_element_by_class('categories', page))))),
            'tags': try_call(lambda: list(map(clean_html, get_elements_by_class('btn', get_elements_by_class('tags', page)[1])))),
            'cast': get_elements_by_class('content-pane-performer', page),
            'availability': 'needs_auth',
            'series': channel_name,