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

Code before:
        for format_id in ['', 'hi', 'webm']:
            video_url = url_or_none(xpath_text(video, f'./{format_id}file'))
            if not video_url:
                continue
            formats.append({
                'url': video_url,
                'filesize': int_or_none(xpath_text(video, f'./{format_id}size')),
            })
        hls_manifest = xpath_text(video, './playback/hls')
        if hls_manifest:
            formats.extend(self._extract_m3u8_formats(
                hls_manifest, video_id, m3u8_id='hls', fatal=False))
        dash_manifest = xpath_text(video, './playback/dash')
        if dash_manifest:
            formats.extend(self._extract_mpd_formats(
                dash_manifest, video_id, mpd_id='dash', fatal=False))

        metadata = self._download_xml(
            f'https://www.ntv.ru/exp/video/{video_id}', video_id, 'Downloading XML metadata', fatal=False)

        return {
            'id': video_id,
            'formats': formats,
            **traverse_obj(metadata, {
                'title': ('{*}title/text()', ..., {unescapeHTML}, any),
                'description': ('{*}description/text()', ..., {unescapeHTML}, any),
                'duration': ('{*}duration/text()', ..., {int_or_none}, any),
                'timestamp': ('{*}create_date/text()', ..., {parse_iso8601}, any),
                'release_timestamp': ('{*}upload_date/text()', ..., {parse_iso8601}, any),
                'modified_timestamp': ('{*}modify_date/text()', ..., {parse_iso8601}, any),
                'tags': ('{*}tag/text()', ..., {str}, {lambda x: x.split(',')}, ..., {str.strip}, filter),
                'view_count': ('{*}stats/views_total/text()', ..., {int_or_none}, any),
                'comment_count': ('{*}stats/comments/text()', ..., {int_or_none}, any),
            }),
            **traverse_obj(player, {
                'title': ('data/title/text()', ..., {unescapeHTML}, any),
                'description': ('data/description/text()', ..., {unescapeHTML}, any),
                'duration': ('data/video/totaltime/text()', ..., {int_or_none}, any),
                'view_count': ('data/video/views/text()', ..., {int_or_none}, any),
                'thumbnail': ('data/video/splash/text()', ..., {url_or_none}, any),
            }),
        }


Review comment (security):
just a note about `unescapeHTML` (that maybe you're already aware of):

it has a [nasty assert](https://github.com/yt-dlp/yt-dlp/blob/5767fb4ab108dddb07fc839a3b0f4d323a7c4bea/yt_dlp/utils/_utils.py#L814) and should always be preceded by `{str}` in a traversal path (or you should just use `{clean_html}` instead)

here it doesn't really matter cuz we're in XML and using `text()`, but this gave me a flashback and I wanted to pass along the pain

Code after:
        for format_id in ['', 'hi', 'webm']:
            video_url = url_or_none(xpath_text(video, f'./{format_id}file'))
            if not video_url:
                continue
            formats.append({
                'url': video_url,
                'filesize': int_or_none(xpath_text(video, f'./{format_id}size')),
            })
        hls_manifest = xpath_text(video, './playback/hls')
        if hls_manifest:
            formats.extend(self._extract_m3u8_formats(
                hls_manifest, video_id, m3u8_id='hls', fatal=False))
        dash_manifest = xpath_text(video, './playback/dash')
        if dash_manifest:
            formats.extend(self._extract_mpd_formats(
                dash_manifest, video_id, mpd_id='dash', fatal=False))

        metadata = self._download_xml(
            f'https://www.ntv.ru/exp/video/{video_id}', video_id, 'Downloading XML metadata', fatal=False)

        return {
            'id': video_id,
            'formats': formats,
            **traverse_obj(metadata, {
                'title': ('{*}title/text()', ..., {str}, {unescapeHTML}, any),
                'description': ('{*}description/text()', ..., {str}, {unescapeHTML}, any),
                'duration': ('{*}duration/text()', ..., {int_or_none}, any),
                'timestamp': ('{*}create_date/text()', ..., {parse_iso8601}, any),
                'release_timestamp': ('{*}upload_date/text()', ..., {parse_iso8601}, any),
                'modified_timestamp': ('{*}modify_date/text()', ..., {parse_iso8601}, any),
                'tags': ('{*}tag/text()', ..., {str}, {lambda x: x.split(',')}, ..., {str.strip}, filter),
                'view_count': ('{*}stats/views_total/text()', ..., {int_or_none}, any),
                'comment_count': ('{*}stats/comments/text()', ..., {int_or_none}, any),
            }),
            **traverse_obj(player, {
                'title': ('data/title/text()', ..., {str}, {unescapeHTML}, any),
                'description': ('data/description/text()', ..., {str}, {unescapeHTML}, any),
                'duration': ('data/video/totaltime/text()', ..., {int_or_none}, any),
                'view_count': ('data/video/views/text()', ..., {int_or_none}, any),
                'thumbnail': ('data/video/splash/text()', ..., {url_or_none}, any),
            }),
        }