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

Code before:
    join_nonempty,
    str_or_none,
    url_or_none,
)
from ..utils.traversal import traverse_obj


class TapTapBaseIE(InfoExtractor):
    _X_UA = 'V=1&PN=WebApp&LANG=zh_CN&VN_CODE=102&LOC=CN&PLT=PC&DS=Android&UID={uuid}&OS=Windows&OSV=10&DT=PC'
    _VIDEO_API = 'https://www.taptap.cn/webapiv2/video-resource/v1/multi-get'
    _INFO_API = None
    _INFO_QUERY_KEY = 'id'
    _DATA_PATH = None
    _ID_PATH = None
    _META_PATH = None

    def _get_api(self, url, video_id, query, **kwargs):
        query = {**query, 'X-UA': self._X_UA.format(uuid=uuid.uuid4())}
        return self._download_json(url, video_id, query=query, **kwargs)['data']

    def _extract_video(self, video_id):
        video_data = self._get_api(self._VIDEO_API, video_id, query={'video_ids': video_id})['list'][0]

        # h265 playlist contains both h265 and h264 formats
        video_url = traverse_obj(video_data, ('play_url', ('url_h265', 'url'), {url_or_none}, any))
        formats = self._extract_m3u8_formats(video_url, video_id)
        for format in formats:
            if re.search(r'^(hev|hvc|hvt)\d', format.get('vcodec', '')):
                format['format_id'] = join_nonempty(format.get('format_id'), 'h265', delim='_')

        return {
            'id': str(video_id),
            'formats': formats,
            **traverse_obj(video_data, ({
                'duration': ('info', 'duration', {int_or_none}),
                'thumbnail': ('thumbnail', ('original_url', 'url'), {url_or_none}),
            }), get_all=False)
        }

    def _real_extract(self, url):
        video_id = self._match_id(url)
        query = {self._INFO_QUERY_KEY: video_id}

        data = traverse_obj(
            self._get_api(self._INFO_API, video_id, query=query), self._DATA_PATH)

        metainfo = traverse_obj(data, self._META_PATH)
        entries = [{
            **metainfo,
            **self._extract_video(id)
        } for id in set(traverse_obj(data, self._ID_PATH))]

Review comment (refactor):
this should be non-fatal or else 1 bad m3u8 url will break playlist extraction

```suggestion
        formats = self._extract_m3u8_formats(video_url, video_id, fatal=False)
```

Code after:
    join_nonempty,
    str_or_none,
    url_or_none,
)
from ..utils.traversal import traverse_obj


class TapTapBaseIE(InfoExtractor):
    _X_UA = 'V=1&PN=WebApp&LANG=zh_CN&VN_CODE=102&LOC=CN&PLT=PC&DS=Android&UID={uuid}&OS=Windows&OSV=10&DT=PC'
    _VIDEO_API = 'https://www.taptap.cn/webapiv2/video-resource/v1/multi-get'
    _INFO_API = None
    _INFO_QUERY_KEY = 'id'
    _DATA_PATH = None
    _ID_PATH = None
    _META_PATH = None

    def _get_api(self, url, video_id, query, **kwargs):
        query = {**query, 'X-UA': self._X_UA.format(uuid=uuid.uuid4())}
        return self._download_json(url, video_id, query=query, **kwargs)['data']

    def _extract_video(self, video_id):
        video_data = self._get_api(self._VIDEO_API, video_id, query={'video_ids': video_id})['list'][0]

        # h265 playlist contains both h265 and h264 formats
        video_url = traverse_obj(video_data, ('play_url', ('url_h265', 'url'), {url_or_none}, any))
        formats = self._extract_m3u8_formats(video_url, video_id, fatal=False)
        for format in formats:
            if re.search(r'^(hev|hvc|hvt)\d', format.get('vcodec', '')):
                format['format_id'] = join_nonempty(format.get('format_id'), 'h265', delim='_')

        return {
            'id': str(video_id),
            'formats': formats,
            **traverse_obj(video_data, ({
                'duration': ('info', 'duration', {int_or_none}),
                'thumbnail': ('thumbnail', ('original_url', 'url'), {url_or_none}),
            }), get_all=False)
        }

    def _real_extract(self, url):
        video_id = self._match_id(url)
        query = {self._INFO_QUERY_KEY: video_id}

        data = traverse_obj(
            self._get_api(self._INFO_API, video_id, query=query), self._DATA_PATH)

        metainfo = traverse_obj(data, self._META_PATH)
        entries = [{
            **metainfo,
            **self._extract_video(id)
        } for id in set(traverse_obj(data, self._ID_PATH))]