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

Code before:
    }, {
        # adult content
        'url': 'https://vod.sooplive.co.kr/player/70395877',
        'only_matching': True,
    }, {
        # subscribers only
        'url': 'https://vod.sooplive.co.kr/player/104647403',
        'only_matching': True,
    }, {
        # private
        'url': 'https://vod.sooplive.co.kr/player/81669846',
        'only_matching': True,
    }]

    def _real_extract(self, url):
        video_id = self._match_id(url)
        data = self._call_api(
            'station/video/a/view', video_id, headers={'Referer': url},
            data=urlencode_postdata({
                'nTitleNo': video_id,
                'nApiLevel': 10,
            }))['data']

        # For subscribers only, call private_auth.php to get CloudFront cookies
        needs_private_auth = traverse_obj(data, ('sub_upload_type', {str}))
        strm_id = traverse_obj(data, ('bj_id', {str}))
        m3u8_url = next((
            file_element.get('file') for file_element in traverse_obj(
                data, ('files', lambda _, v: url_or_none(v.get('file'))))
            if file_element.get('file') and determine_ext(file_element.get('file')) == 'm3u8'), None)

        if needs_private_auth and m3u8_url and strm_id:
            self._request_webpage(
                _cloudfront_auth_request(m3u8_url, strm_id, video_id, url),
                video_id, note=False, fatal=False)

        error_code = traverse_obj(data, ('code', {int}))
        if error_code == -6221:
            raise ExtractorError('The VOD does not exist', expected=True)
        elif error_code == -6205:
            raise ExtractorError('This VOD is private', expected=True)

        common_info = traverse_obj(data, {
            'title': ('title', {str}),
            'uploader': ('writer_nick', {str}),
            'uploader_id': ('bj_id', {str}),
            'duration': ('total_file_duration', {int_or_none(scale=1000)}),
            'thumbnails': ('thumb', {self._fixup_thumb}),
        })

        entries = []

Review comment (security):
Let's require `strm_id` if `needs_private_auth`

(import `require` from `..utils.traversal`)
```suggestion
        strm_id = None
        # For subscriber-only VODs, we need to call private_auth.php to get CloudFront cookies
        needs_private_auth = traverse_obj(data, ('sub_upload_type', {str}))
        if needs_private_auth:
            strm_id = traverse_obj(data, ('bj_id', {str}, {require('stream ID')}))
```

Code after:
        # adult content
        'url': 'https://vod.sooplive.co.kr/player/70395877',
        'only_matching': True,
    }, {
        # subscribers only
        'url': 'https://vod.sooplive.co.kr/player/104647403',
        'only_matching': True,
    }, {
        # private
        'url': 'https://vod.sooplive.co.kr/player/81669846',
        'only_matching': True,
    }]

    def _real_extract(self, url):
        video_id = self._match_id(url)
        data = self._call_api(
            'station/video/a/view', video_id, headers={'Referer': url},
            data=urlencode_postdata({
                'nTitleNo': video_id,
                'nApiLevel': 10,
            }))['data']

        initial_refresh_time = 0
        strm_id = None
        # For subscriber-only VODs, we need to call private_auth.php to get CloudFront cookies
        needs_private_auth = traverse_obj(data, ('sub_upload_type', {str}))
        if needs_private_auth:
            strm_id = traverse_obj(data, ('bj_id', {str}, {require('stream ID')}))

        error_code = traverse_obj(data, ('code', {int}))
        if error_code == -6221:
            raise ExtractorError('The VOD does not exist', expected=True)
        elif error_code == -6205:
            raise ExtractorError('This VOD is private', expected=True)

        common_info = traverse_obj(data, {
            'title': ('title', {str}),
            'uploader': ('writer_nick', {str}),
            'uploader_id': ('bj_id', {str}),
            'duration': ('total_file_duration', {int_or_none(scale=1000)}),
            'thumbnails': ('thumb', {self._fixup_thumb}),
        })

        entries = []
        for file_num, file_element in enumerate(
                traverse_obj(data, ('files', lambda _, v: url_or_none(v['file']))), start=1):
            file_url = file_element['file']
            if determine_ext(file_url) == 'm3u8':
                if needs_private_auth:
                    self._request_webpage(
                        _cloudfront_auth_request(file_url, strm_id, video_id, url),