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

Code before:

    def _real_extract(self, url):
        server, video_id = self._match_valid_url(url).group('server', 'id')

        webpage, urlh = self._download_webpage_handle(f'{server}/s/{video_id}', video_id, 'Downloading webpage')

        if self._search_regex(r'<label[^>]+?for="(password)"', webpage, 'password field',
                              fatal=False, default=None):
            # Password protected
            webpage, urlh = self._verify_video_password(webpage, urlh.geturl(), video_id)

        hidden_inputs = self._hidden_inputs(webpage)
        title = hidden_inputs['filename']

        return {
            'id': video_id,
            'title': title,
            'url': url_or_none(hidden_inputs.get('downloadURL') or self._extend_to_download_url(urlh.geturl())),
            'ext': determine_ext(title),
        }

    def _extend_to_download_url(self, url: str) -> str:
        # Adds /download to the end of the URL path
        url_parts = list(urllib.parse.urlparse(url))
        url_parts[2] = url_parts[2].rstrip('/') + '/download'
        return urllib.parse.urlunparse(url_parts)

    def _verify_video_password(self, webpage, url, video_id):
        password = self._downloader.params.get('videopassword')
        if password is None:
            raise ExtractorError(
                'This video is protected by a password, use the --video-password option',
                expected=True
            )

        data = urlencode_postdata(
            {
                'requesttoken': self._search_regex(
                    r'<input[^>]+?name="requesttoken" value="([^\"]+)"', webpage, 'requesttoken'
                ),
                'password': password,
            }
        )

        validation_response, urlh = self._download_webpage_handle(
            url, video_id, note='Validating Password...', errnote='Wrong password?', data=data
        )

        if self._search_regex(r'<label[^>]+?for="(password)"', validation_response,
                              'password field', fatal=False, default=None):
            # Still password protected

Review comment (suggestion):
```suggestion
    def _extend_to_download_url(self, url):
        parsed = urllib.parse.urlparse(url)
        return parsed._replace(
            path=urllib.parse.urljoin(parsed.path, 'download')).geturl()
```
idk if this should even be a function

Code after:
                'id': 'WNDuFu0XuFtmm3f',
                'ext': 'mp4',
                'title': 'CmvpJST.mp4',
            },
            'params': {
                'videopassword': '12345',
            },
        },
    ]

    def _real_extract(self, url):
        video_id = self._match_id(url)
        webpage, urlh = self._download_webpage_handle(url, video_id)

        if re.search(r'<label[^>]+for="password"', webpage):
            webpage = self._verify_video_password(webpage, urlh.geturl(), video_id)

        hidden_inputs = self._hidden_inputs(webpage)
        title = hidden_inputs.get('filename')
        parsed_url = urllib.parse.urlparse(url)

        return {
            'id': video_id,
            'title': title,
            'url': url_or_none(hidden_inputs.get('downloadURL')) or parsed_url._replace(
                path=urllib.parse.urljoin(parsed_url.path, 'download')).geturl(),
            'ext': determine_ext(title),
        }

    def _verify_video_password(self, webpage, url, video_id):
        password = self.get_param('videopassword')
        if password is None:
            raise ExtractorError(
                'This video is protected by a password, use the --video-password option',
                expected=True)

        validation_response = self._download_webpage(
            url, video_id, 'Validating Password', 'Wrong password?',
            data=urlencode_postdata({
                'requesttoken': self._hidden_inputs(webpage)['requesttoken'],
                'password': password,
            }))

        if re.search(r'<label[^>]+for="password"', validation_response):
            warning = self._search_regex(
                r'<div[^>]+class="warning">([^<]*)</div>', validation_response,
                'warning', default='The password is wrong')
            raise ExtractorError(f'Opening the video failed, {self.IE_NAME} said: {warning!r}', expected=True)
        return validation_response