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

Code before:
        (?P<server>https?://{_INSTANCES_RE})/s/
        (?P<id>[\w\-\.]+)'''

    _TESTS = [
        {
            'url': 'https://ruhr-uni-bochum.sciebo.de/s/wWhqZzh9jTumVFN',
            'info_dict': {
                'id': 'wWhqZzh9jTumVFN',
                'ext': 'mp4',
                'title': 'CmvpJST.mp4',
            },
        },
    ]

    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(

Review comment (suggestion):
```suggestion
        title = hidden_inputs.get('filename')
```

Code after:

    _TESTS = [
        {
            'url': 'https://ruhr-uni-bochum.sciebo.de/s/wWhqZzh9jTumVFN',
            'info_dict': {
                'id': 'wWhqZzh9jTumVFN',
                'ext': 'mp4',
                'title': 'CmvpJST.mp4',
            },
        },
        {
            'url': 'https://ruhr-uni-bochum.sciebo.de/s/WNDuFu0XuFtmm3f',
            'info_dict': {
                '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?',