code:
import json

from .common import InfoExtractor
from .gigya import GigyaBaseIE
from ..compat import compat_HTTPError
from ..utils import (
    ExtractorError,
    clean_html,
    extract_attributes,
    float_or_none,
    get_element_by_class,
    int_or_none,
    merge_dicts,
    str_or_none,
    strip_or_none,
    url_or_none,
    urlencode_postdata
)

class CanvasIE(InfoExtractor):
    _VALID_URL = r'https?://mediazone\.vrt\.be/api/v1/(?P<site_id>canvas|een|ketnet|vrt(?:video|nieuws)|sporza|dako)/assets/(?P<id>[^/?#&]+)'
    _TESTS = [{
        'url': 'https://mediazone.vrt.be/api/v1/ketnet/assets/md-ast-4ac54990-ce66-4d00-a8ca-9eac86f4c475',
        'md5': '37b2b7bb9b3dcaa05b67058dc3a714a9',
        'info_dict': {
            'id': 'md-ast-4ac54990-ce66-4d00-a8ca-9eac86f4c475',
            'display_id': 'md-ast-4ac54990-ce66-4d00-a8ca-9eac86f4c475',
            'ext': 'mp4',
            'title': 'Nachtwacht: De Greystook',
            'description': 'Nachtwacht: De Greystook',
            'thumbnail': r're:^https?://.*\.jpg$',
            'duration': 1468.02,
        },
        'expected_warnings': ['is not a supported codec'],
    }, {
        'url': 'https://mediazone.vrt.be/api/v1/canvas/assets/mz-ast-5e5f90b6-2d72-4c40-82c2-e134f884e93e',
        'only_matching': True,
    }]
    _GEO_BYPASS = False
    _HLS_ENTRY_PROTOCOLS_MAP = {
        'HLS': 'm3u8_native',
        'HLS_AES': 'm3u8_native',
    }
    _REST_API_BASE = 'https://media-services-public.vrt.be/vualto-video-aggregator-web/rest/external/v2'

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

        data = None
        if site_id != 'vrtvideo':
            # Old API endpoint, serves more formats but may fail for some videos
            data = self._download_json(
                'https://mediazone.vrt.be/api/v1/%s/assets/%s'
                % (site_id, video_id), video_id, 'Downloading asset JSON',
                'Unable to download asset JSON', fatal=False)

        # New API endpoint
        if not data:
            vrtnutoken = self._download_json('https://token.vrt.be/refreshtoken',
                                             video_id, note='refreshtoken: Retrieve vrtnutoken',
                                             errnote='refreshtoken failed')['vrtnutoken']
            headers = self.geo_verification_headers()
            headers.update({'Content-Type': 'application/json; charset=utf-8'})
            vrtPlayerToken = self._download_json(
                '%s/tokens' % self._REST_API_BASE, video_id,
                'Downloading token', headers=headers, data=json.dumps({
                    'identityToken': vrtnutoken
                }).encode('utf-8'))['vrtPlayerToken']
            data = self._download_json(
                '%s/videos/%s' % (self._REST_API_BASE, video_id),
                video_id, 'Downloading video JSON', query={
                    'vrtPlayerToken': vrtPlayerToken,
                    'client': 'null',
                }, expected_status=400)
            if 'title' not in data:
                code = data.get('code')
                if code == 'AUTHENTICATION_REQUIRED':
                    self.raise_login_required()
                elif code == 'INVALID_LOCATION':
                    self.raise_geo_restricted(countries=['BE'])
                raise ExtractorError(data.get('message') or code, expected=True)

        # Note: The title may be an empty string
        title = data['title'] or f'{site_id} {video_id}'
        description = data.get('description')

        formats = []
        subtitles = {}
        for target in data['targetUrls']:
            format_url, format_type = url_or_none(target.get('url')), str_or_none(target.get('type'))
            if not format_url or not format_type:
                continue
            format_type = format_type.upper()
            if format_type in self._HLS_ENTRY_PROTOCOLS_MAP:
                fmts, subs = self._extract_m3u8_formats_and_subtitles(
                    format_url, video_id, 'mp4', self._HLS_ENTRY_PROTOCOLS_MAP[format_type],
                    m3u8_id=format_type, fatal=False)
                formats.extend(fmts)
                subtitles = self._merge_subtitles(subtitles, subs)
            elif format_type == 'HDS':
                formats.extend(self._extract_f4m_formats(
                    format_url, video_id, f4m_id=format_type, fatal=False))
            elif format_type == 'MPEG_DASH':
                fmts, subs = self._extract_mpd_formats_and_subtitles(
                    format_url, video_id, mpd_id=format_type, fatal=False)
                formats.extend(fmts)
                subtitles = self._merge_subtitles(subtitles, subs)
            elif format_type == 'HSS':
                fmts, subs = self._extract_ism_formats_and_subtitles(
                    format_url, video_id, ism_id='mss', fatal=False)
                formats.extend(fmts)
                subtitles = self._merge_subtitles(subtitles, subs)
            else:
                formats.append({
                    'format_id': format_type,
                    'url': format_url,
                })
        self._sort_formats(formats)

        subtitle_urls = data.get('subtitleUrls')
        if isinstance(subtitle_urls, list):
            for subtitle in subtitle_urls:
                subtitle_url = subtitle.get('url')
                if subtitle_url and subtitle.get('type') == 'CLOSED':
                    subtitles.setdefault('nl', []).append({'url': subtitle_url})

        return {
            'id': video_id,
            'display_id': video_id,
            'title': title,
            'description': description,
            'formats': formats,
            'duration': float_or_none(data.get('duration'), 1000),
            'thumbnail': data.get('posterImageUrl'),
            'subtitles': subtitles,
        }

class CanvasEenIE(InfoExtractor):
    IE_DESC = 'canvas.be and een.be'
    _VALID_URL = r'https?://(?:www\.)?(?P<site_id>canvas|een)\.be/(?:[^/]+/)*(?P<id>[^/?#&]+)'
    _TESTS = [{
        'url': 'http://www.canvas.be/video/de-afspraak/najaar-2015/de-afspraak-veilt-voor-de-warmste-week',
        'md5': 'ed66976748d12350b118455979cca293',
        'info_dict': {
            'id': 'mz-ast-5e5f90b6-2d72-4c40-82c2-e134f884e93e',
            'display_id': 'de-afspraak-veilt-voor-de-warmste-week',
            'ext': 'flv',
            'title': 'De afspraak veilt voor de Warmste Week',
            'description': 'md5:24cb860c320dc2be7358e0e5aa317ba6',
            'thumbnail': r're:^https?://.*\.jpg$',
            'duration': 49.02,
        },
        'expected_warnings': ['is not a supported codec'],
    }, {
        # with subtitles
        'url': 'http://www.canvas.be/video/panorama/2016/pieter-0167',
        'info_dict': {
            'id': 'mz-ast-5240ff21-2d30-4101-bba6-92b5ec67c625',
            'display_id': 'pieter-0167',
            'ext': 'mp4',
            'title': 'Pieter 0167',
            'description': 'md5:943cd30f48a5d29ba02c3a104dc4ec4e',
            'thumbnail': r're:^https?://.*\.jpg$',
            'duration': 2553.08,
            'subtitles': {
                'nl': [{
                    'ext': 'vtt',
                }],
            },
        },
        'params': {
            'skip_download': True,
        },
        'skip': 'Pagina niet gevonden',
    }, {
        'url': 'https://www.een.be/thuis/emma-pakt-thilly-aan',
        'info_dict': {
            'id': 'md-ast-3a24ced2-64d7-44fb-b4ed-ed1aafbf90b8',
            'display_id': 'emma-pakt-thilly-aan',
            'ext': 'mp4',
            'title': 'Emma pakt Thilly aan',
            'description': 'md5:c5c9b572388a99b2690030afa3f3bad7',
            'thumbnail': r're:^https?://.*\.jpg$',
            'duration': 118.24,
        },
        'params': {
            'skip_download': True,
        },
        'expected_warnings': ['is not a supported codec'],
    }, {
        'url': 'https://www.canvas.be/check-point/najaar-2016/de-politie-uw-vriend',
        'only_matching': True,
    }]

    def _real_extract(self, url):
        mobj = self._match_valid_url(url)
        site_id, display_id = mobj.group('site_id'), mobj.group('id')

        webpage = self._download_webpage(url, display_id)

        title = strip_or_none(self._search_regex(
            r'<h1[^>]+class="video__body__header__title"[^>]*>(.+?)</h1>',
            webpage, 'title', default=None) or self._og_search_title(
            webpage, default=None))

        video_id = self._html_search_regex(
            r'data-video=(["\'])(?P<id>(?:(?!\1).)+)\1', webpage, 'video id',
            group='id')

        return {
            '_type': 'url_transparent',
            'url': 'https://mediazone.vrt.be/api/v1/%s/assets/%s' % (site_id, video_id),
            'ie_key': CanvasIE.ie_key(),
            'id': video_id,
            'display_id': display_id,
            'title': title,
            'description': self._og_search_description(webpage),
        }

class VrtNUIE(GigyaBaseIE):
    IE_DESC = 'VrtNU.be'
    _VALID_URL = r'https?://(?:www\.)?vrt\.be/vrtnu/a-z/(?:[^/]+/){2}(?P<id>[^/?#&]+)'
    _TESTS = [{
        # Available via old API endpoint
        'url': 'https://www.vrt.be/vrtnu/a-z/postbus-x/1989/postbus-x-s1989a1/',
        'info_dict': {
            'id': 'pbs-pub-e8713dac-899e-41de-9313-81269f4c04ac$vid-90c932b1-e21d-4fb8-99b1-db7b49cf74de',
            'ext': 'mp4',
            'title': 'Postbus X - Aflevering 1 (Seizoen 1989)',
            'description': 'md5:b704f669eb9262da4c55b33d7c6ed4b7',
            'duration': 1457.04,
            'thumbnail': r're:^https?://.*\.jpg$',
            'series': 'Postbus X',
            'season': 'Seizoen 1989',
            'season_number': 1989,
            'episode': 'De zwarte weduwe',
            'episode_number': 1,
            'timestamp': 1595822400,
            'upload_date': '20200727',
        },
        'skip': 'This video is only available for registered users',
        'expected_warnings': ['is not a supported codec'],
    }, {
        # Only available via new API endpoint
        'url': 'https://www.vrt.be/vrtnu/a-z/kamp-waes/1/kamp-waes-s1a5/',
        'info_dict': {
            'id': 'pbs-pub-0763b56c-64fb-4d38-b95b-af60bf433c71$vid-ad36a73c-4735-4f1f-b2c0-a38e6e6aa7e1',
            'ext': 'mp4',
            'title': 'Aflevering 5',
            'description': 'Wie valt door de mand tijdens een missie?',
            'duration': 2967.06,
            'season': 'Season 1',
            'season_number': 1,
            'episode_number': 5,
        },
        'skip': 'This video is only available for registered users',
        'expected_warnings': ['Unable to download asset JSON', 'is not a supported codec', 'Unknown MIME type'],
    }]
    _NETRC_MACHINE = 'vrtnu'
    _APIKEY = '3_0Z2HujMtiWq_pkAjgnS2Md2E11a1AwZjYiBETtwNE-EoEHDINgtnvcAOpNgmrVGy'
    _CONTEXT_ID = 'R3595707040'

    def _perform_login(self, username, password):
        auth_info = self._gigya_login({
            'APIKey': self._APIKEY,
            'targetEnv': 'jssdk',
            'loginID': username,
            'password': password,
            'authMode': 'cookie',
        })

        if auth_info.get('errorDetails'):
            raise ExtractorError('Unable to login: VrtNU said: ' + auth_info.get('errorDetails'), expected=True)

        # Sometimes authentication fails for no good reason, retry
        login_attempt = 1
        while login_attempt <= 3:
            try:
                self._request_webpage('https://token.vrt.be/vrtnuinitlogin',
                                      None, note='Requesting XSRF Token', errnote='Could not get XSRF Token',
                                      query={'provider': 'site', 'destination': 'https://www.vrt.be/vrtnu/'})

                post_data = {
                    'UID': auth_info['UID'],
                    'UIDSignature': auth_info['UIDSignature'],
                    'signatureTimestamp': auth_info['signatureTimestamp'],
                    '_csrf': self._get_cookies('https://login.vrt.be').get('OIDCXSRF').value,
                }

                self._request_webpage(
                    'https://login.vrt.be/perform_login',
                    None, note='Performing login', errnote='perform login failed',
                    headers={}, query={
                        'client_id': 'vrtnu-site'
                    }, data=urlencode_postdata(post_data))

            except ExtractorError as e:
                if isinstance(e.cause, compat_HTTPError) and e.cause.code == 401:
                    login_attempt += 1
                    self.report_warning('Authentication failed')
                    self._sleep(1, None, msg_template='Waiting for %(timeout)s seconds before trying again')
                else:
                    raise e
            else:
                break

    def _real_extract(self, url):
        display_id = self._match_id(url)

        webpage = self._download_webpage(url, display_id)

        attrs = extract_attributes(self._search_regex(
            r'(<nui-media[^>]+>)', webpage, 'media element'))
        video_id = attrs['videoid']
        publication_id = attrs.get('publicationid')
        if publication_id:
            video_id = publication_id + '$' + video_id

        page = (self._parse_json(self._search_regex(
            r'digitalData\s*=\s*({.+?});', webpage, 'digial data',
            default='{}'), video_id, fatal=False) or {}).get('page') or {}

        info = self._search_json_ld(webpage, display_id, default={})
        return merge_dicts(info, {
            '_type': 'url_transparent',
            'url': 'https://mediazone.vrt.be/api/v1/vrtvideo/assets/%s' % video_id,
            'ie_key': CanvasIE.ie_key(),
            'id': video_id,
            'display_id': display_id,
            'season_number': int_or_none(page.get('episode_season')),
        })

class DagelijkseKostIE(InfoExtractor):
    IE_DESC = 'dagelijksekost.een.be'
    _VALID_URL = r'https?://dagelijksekost\.een\.be/gerechten/(?P<id>[^/?#&]+)'
    _TEST = {
        'url': 'https://dagelijksekost.een.be/gerechten/hachis-parmentier-met-witloof',
        'md5': '30bfffc323009a3e5f689bef6efa2365',
        'info_dict': {
            'id': 'md-ast-27a4d1ff-7d7b-425e-b84f-a4d227f592fa',
            'display_id': 'hachis-parmentier-met-witloof',
            'ext': 'mp4',
            'title': 'Hachis parmentier met witloof',
            'description': 'md5:9960478392d87f63567b5b117688cdc5',
            'thumbnail': r're:^https?://.*\.jpg$',
            'duration': 283.02,
        },
        'expected_warnings': ['is not a supported codec'],
    }

    def _real_extract(self, url):
        display_id = self._match_id(url)
        webpage = self._download_webpage(url, display_id)

        title = strip_or_none(get_element_by_class(
            'dish-metadata__title', webpage
        ) or self._html_search_meta(
            'twitter:title', webpage))

        description = clean_html(get_element_by_class(
            'dish-description', webpage)
        ) or self._html_search_meta(
            ('description', 'twitter:description', 'og:description'),
            webpage)

        video_id = self._html_search_regex(
            r'data-url=(["\'])(?P<id>(?:(?!\1).)+)\1', webpage, 'video id',
            group='id')

        return {
            '_type': 'url_transparent',
            'url': 'https://mediazone.vrt.be/api/v1/dako/assets/%s' % video_id,
            'ie_key': CanvasIE.ie_key(),
            'id': video_id,
            'display_id': display_id,
            'title': title,
            'description': description,
        }


code:
from .common import InfoExtractor
from ..compat import compat_str
from ..utils import (
    format_field,
    float_or_none,
    int_or_none,
    try_get,
)

from .videomore import VideomoreIE

class CarambaTVIE(InfoExtractor):
    _VALID_URL = r'(?:carambatv:|https?://video1\.carambatv\.ru/v/)(?P<id>\d+)'
    _TESTS = [{
        'url': 'http://video1.carambatv.ru/v/191910501',
        'md5': '2f4a81b7cfd5ab866ee2d7270cb34a2a',
        'info_dict': {
            'id': '191910501',
            'ext': 'mp4',
            'title': '[BadComedian] - Разборка в Маниле (Абсолютный обзор)',
            'thumbnail': r're:^https?://.*\.jpg',
            'duration': 2678.31,
        },
    }, {
        'url': 'carambatv:191910501',
        'only_matching': True,
    }]

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

        video = self._download_json(
            'http://video1.carambatv.ru/v/%s/videoinfo.js' % video_id,
            video_id)

        title = video['title']

        base_url = video.get('video') or 'http://video1.carambatv.ru/v/%s/' % video_id

        formats = [{
            'url': base_url + f['fn'],
            'height': int_or_none(f.get('height')),
            'format_id': format_field(f, 'height', '%sp'),
        } for f in video['qualities'] if f.get('fn')]
        self._sort_formats(formats)

        thumbnail = video.get('splash')
        duration = float_or_none(try_get(
            video, lambda x: x['annotations'][0]['end_time'], compat_str))

        return {
            'id': video_id,
            'title': title,
            'thumbnail': thumbnail,
            'duration': duration,
            'formats': formats,
        }

class CarambaTVPageIE(InfoExtractor):
    _VALID_URL = r'https?://carambatv\.ru/(?:[^/]+/)+(?P<id>[^/?#&]+)'
    _TEST = {
        'url': 'http://carambatv.ru/movie/bad-comedian/razborka-v-manile/',
        'md5': 'a49fb0ec2ad66503eeb46aac237d3c86',
        'info_dict': {
            'id': '475222',
            'ext': 'flv',
            'title': '[BadComedian] - Разборка в Маниле (Абсолютный обзор)',
            'thumbnail': r're:^https?://.*\.jpg',
            # duration reported by videomore is incorrect
            'duration': int,
        },
        'add_ie': [VideomoreIE.ie_key()],
    }

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

        webpage = self._download_webpage(url, video_id)

        videomore_url = VideomoreIE._extract_url(webpage)
        if not videomore_url:
            videomore_id = self._search_regex(
                r'getVMCode\s*\(\s*["\']?(\d+)', webpage, 'videomore id',
                default=None)
            if videomore_id:
                videomore_url = 'videomore:%s' % videomore_id
        if videomore_url:
            title = self._og_search_title(webpage)
            return {
                '_type': 'url_transparent',
                'url': videomore_url,
                'ie_key': VideomoreIE.ie_key(),
                'title': title,
            }

        video_url = self._og_search_property('video:iframe', webpage, default=None)

        if not video_url:
            video_id = self._search_regex(
                r'(?:video_id|crmb_vuid)\s*[:=]\s*["\']?(\d+)',
                webpage, 'video id')
            video_url = 'carambatv:%s' % video_id

        return self.url_result(video_url, CarambaTVIE.ie_key())

repo_name: Lesmiscore/ytdl


code:
from .turner import TurnerBaseIE
from ..utils import int_or_none

class CartoonNetworkIE(TurnerBaseIE):
    _VALID_URL = r'https?://(?:www\.)?cartoonnetwork\.com/video/(?:[^/]+/)+(?P<id>[^/?#]+)-(?:clip|episode)\.html'
    _TEST = {
        'url': 'https://www.cartoonnetwork.com/video/ben-10/how-to-draw-upgrade-episode.html',
        'info_dict': {
            'id': '6e3375097f63874ebccec7ef677c1c3845fa850e',
            'ext': 'mp4',
            'title': 'How to Draw Upgrade',
            'description': 'md5:2061d83776db7e8be4879684eefe8c0f',
        },
        'params': {
            # m3u8 download
            'skip_download': True,
        },
    }

    def _real_extract(self, url):
        display_id = self._match_id(url)
        webpage = self._download_webpage(url, display_id)

        def find_field(global_re, name, content_re=None, value_re='[^"]+', fatal=False):
            metadata_re = ''
            if content_re:
                metadata_re = r'|video_metadata\.content_' + content_re
            return self._search_regex(
                r'(?:_cnglobal\.currentVideo\.%s%s)\s*=\s*"(%s)";' % (global_re, metadata_re, value_re),
                webpage, name, fatal=fatal)

        media_id = find_field('mediaId', 'media id', 'id', '[0-9a-f]{40}', True)
        title = find_field('episodeTitle', 'title', '(?:episodeName|name)', fatal=True)

        info = self._extract_ngtv_info(
            media_id, {'networkId': 'cartoonnetwork'}, {
                'url': url,
                'site_name': 'CartoonNetwork',
                'auth_required': find_field('authType', 'auth type') != 'unauth',
            })

        series = find_field(
            'propertyName', 'series', 'showName') or self._html_search_meta('partOfSeries', webpage)
        info.update({
            'id': media_id,
            'display_id': display_id,
            'title': title,
            'description': self._html_search_meta('description', webpage),
            'series': series,
            'episode': title,
        })

        for field in ('season', 'episode'):
            field_name = field + 'Number'
            info[field + '_number'] = int_or_none(find_field(
                field_name, field + ' number', value_re=r'\d+') or self._html_search_meta(field_name, webpage))

        return info


code:
import re
import json
import base64
import time

from .common import InfoExtractor
from ..compat import (
    compat_str,
)
from ..utils import (
    int_or_none,
    join_nonempty,
    js_to_json,
    orderedSet,
    smuggle_url,
    strip_or_none,
    try_get,
    ExtractorError,
)

class CBCIE(InfoExtractor):
    IE_NAME = 'cbc.ca'
    _VALID_URL = r'https?://(?:www\.)?cbc\.ca/(?!player/)(?:[^/]+/)+(?P<id>[^/?#]+)'
    _TESTS = [{
        # with mediaId
        'url': 'http://www.cbc.ca/22minutes/videos/clips-season-23/don-cherry-play-offs',
        'md5': '97e24d09672fc4cf56256d6faa6c25bc',
        'info_dict': {
            'id': '2682904050',
            'ext': 'mp4',
            'title': 'Don Cherry – All-Stars',
            'description': 'Don Cherry has a bee in his bonnet about AHL player John Scott because that guy’s got heart.',
            'timestamp': 1454463000,
            'upload_date': '20160203',
            'uploader': 'CBCC-NEW',
        },
        'skip': 'Geo-restricted to Canada',
    }, {
        # with clipId, feed available via tpfeed.cbc.ca and feed.theplatform.com
        'url': 'http://www.cbc.ca/22minutes/videos/22-minutes-update/22-minutes-update-episode-4',
        'md5': '162adfa070274b144f4fdc3c3b8207db',
        'info_dict': {
            'id': '2414435309',
            'ext': 'mp4',
            'title': '22 Minutes Update: What Not To Wear Quebec',
            'description': "This week's latest Canadian top political story is What Not To Wear Quebec.",
            'upload_date': '20131025',
            'uploader': 'CBCC-NEW',
            'timestamp': 1382717907,
        },
        'skip': 'No longer available',
    }, {
        # with clipId, feed only available via tpfeed.cbc.ca
        'url': 'http://www.cbc.ca/archives/entry/1978-robin-williams-freestyles-on-90-minutes-live',
        'md5': '0274a90b51a9b4971fe005c63f592f12',
        'info_dict': {
            'id': '2487345465',
            'ext': 'mp4',
            'title': 'Robin Williams freestyles on 90 Minutes Live',
            'description': 'Wacky American comedian Robin Williams shows off his infamous "freestyle" comedic talents while being interviewed on CBC\'s 90 Minutes Live.',
            'upload_date': '19780210',
            'uploader': 'CBCC-NEW',
            'timestamp': 255977160,
        },
    }, {
        # multiple iframes
        'url': 'http://www.cbc.ca/natureofthings/blog/birds-eye-view-from-vancouvers-burrard-street-bridge-how-we-got-the-shot',
        'playlist': [{
            'md5': '377572d0b49c4ce0c9ad77470e0b96b4',
            'info_dict': {
                'id': '2680832926',
                'ext': 'mp4',
                'title': 'An Eagle\'s-Eye View Off Burrard Bridge',
                'description': 'Hercules the eagle flies from Vancouver\'s Burrard Bridge down to a nearby park with a mini-camera strapped to his back.',
                'upload_date': '20160201',
                'timestamp': 1454342820,
                'uploader': 'CBCC-NEW',
            },
        }, {
            'md5': '415a0e3f586113894174dfb31aa5bb1a',
            'info_dict': {
                'id': '2658915080',
                'ext': 'mp4',
                'title': 'Fly like an eagle!',
                'description': 'Eagle equipped with a mini camera flies from the world\'s tallest tower',
                'upload_date': '20150315',
                'timestamp': 1426443984,
                'uploader': 'CBCC-NEW',
            },
        }],
        'skip': 'Geo-restricted to Canada',
    }, {
        # multiple CBC.APP.Caffeine.initInstance(...)
        'url': 'http://www.cbc.ca/news/canada/calgary/dog-indoor-exercise-winter-1.3928238',
        'info_dict': {
            'title': 'Keep Rover active during the deep freeze with doggie pushups and other fun indoor tasks',
            'id': 'dog-indoor-exercise-winter-1.3928238',
            'description': 'md5:c18552e41726ee95bd75210d1ca9194c',
        },
        'playlist_mincount': 6,
    }]

    @classmethod
    def suitable(cls, url):
        return False if CBCPlayerIE.suitable(url) else super(CBCIE, cls).suitable(url)

    def _extract_player_init(self, player_init, display_id):
        player_info = self._parse_json(player_init, display_id, js_to_json)
        media_id = player_info.get('mediaId')
        if not media_id:
            clip_id = player_info['clipId']
            feed = self._download_json(
                'http://tpfeed.cbc.ca/f/ExhSPC/vms_5akSXx4Ng_Zn?byCustomValue={:mpsReleases}{%s}' % clip_id,
                clip_id, fatal=False)
            if feed:
                media_id = try_get(feed, lambda x: x['entries'][0]['guid'], compat_str)
            if not media_id:
                media_id = self._download_json(
                    'http://feed.theplatform.com/f/h9dtGB/punlNGjMlc1F?fields=id&byContent=byReleases%3DbyId%253D' + clip_id,
                    clip_id)['entries'][0]['id'].split('/')[-1]
        return self.url_result('cbcplayer:%s' % media_id, 'CBCPlayer', media_id)

    def _real_extract(self, url):
        display_id = self._match_id(url)
        webpage = self._download_webpage(url, display_id)
        title = (self._og_search_title(webpage, default=None)
                 or self._html_search_meta('twitter:title', webpage, 'title', default=None)
                 or self._html_extract_title(webpage))
        entries = [
            self._extract_player_init(player_init, display_id)
            for player_init in re.findall(r'CBC\.APP\.Caffeine\.initInstance\(({.+?})\);', webpage)]
        media_ids = []
        for media_id_re in (
                r'<iframe[^>]+src="[^"]+?mediaId=(\d+)"',
                r'<div[^>]+\bid=["\']player-(\d+)',
                r'guid["\']\s*:\s*["\'](\d+)'):
            media_ids.extend(re.findall(media_id_re, webpage))
        entries.extend([
            self.url_result('cbcplayer:%s' % media_id, 'CBCPlayer', media_id)
            for media_id in orderedSet(media_ids)])
        return self.playlist_result(
            entries, display_id, strip_or_none(title),
            self._og_search_description(webpage))

class CBCPlayerIE(InfoExtractor):
    IE_NAME = 'cbc.ca:player'
    _VALID_URL = r'(?:cbcplayer:|https?://(?:www\.)?cbc\.ca/(?:player/play/|i/caffeine/syndicate/\?mediaId=))(?P<id>\d+)'
    _TESTS = [{
        'url': 'http://www.cbc.ca/player/play/2683190193',
        'md5': '64d25f841ddf4ddb28a235338af32e2c',
        'info_dict': {
            'id': '2683190193',
            'ext': 'mp4',
            'title': 'Gerry Runs a Sweat Shop',
            'description': 'md5:b457e1c01e8ff408d9d801c1c2cd29b0',
            'timestamp': 1455071400,
            'upload_date': '20160210',
            'uploader': 'CBCC-NEW',
        },
        'skip': 'Geo-restricted to Canada',
    }, {
        # Redirected from http://www.cbc.ca/player/AudioMobile/All%20in%20a%20Weekend%20Montreal/ID/2657632011/
        'url': 'http://www.cbc.ca/player/play/2657631896',
        'md5': 'e5e708c34ae6fca156aafe17c43e8b75',
        'info_dict': {
            'id': '2657631896',
            'ext': 'mp3',
            'title': 'CBC Montreal is organizing its first ever community hackathon!',
            'description': 'The modern technology we tend to depend on so heavily, is never without it\'s share of hiccups and headaches. Next weekend - CBC Montreal will be getting members of the public for its first Hackathon.',
            'timestamp': 1425704400,
            'upload_date': '20150307',
            'uploader': 'CBCC-NEW',
        },
    }, {
        'url': 'http://www.cbc.ca/player/play/2164402062',
        'md5': '33fcd8f6719b9dd60a5e73adcb83b9f6',
        'info_dict': {
            'id': '2164402062',
            'ext': 'mp4',
            'title': 'Cancer survivor four times over',
            'description': 'Tim Mayer has beaten three different forms of cancer four times in five years.',
            'timestamp': 1320410746,
            'upload_date': '20111104',
            'uploader': 'CBCC-NEW',
        },
    }]

    def _real_extract(self, url):
        video_id = self._match_id(url)
        return {
            '_type': 'url_transparent',
            'ie_key': 'ThePlatform',
            'url': smuggle_url(
                'http://link.theplatform.com/s/ExhSPC/media/guid/2655402169/%s?mbr=true&formats=MPEG4,FLV,MP3' % video_id, {
                    'force_smil_url': True
                }),
            'id': video_id,
        }

class CBCGemIE(InfoExtractor):
    IE_NAME = 'gem.cbc.ca'
    _VALID_URL = r'https?://gem\.cbc\.ca/media/(?P<id>[0-9a-z-]+/s[0-9]+[a-z][0-9]+)'
    _TESTS = [{
        # This is a normal, public, TV show video
        'url': 'https://gem.cbc.ca/media/schitts-creek/s06e01',
        'md5': '93dbb31c74a8e45b378cf13bd3f6f11e',
        'info_dict': {
            'id': 'schitts-creek/s06e01',
            'ext': 'mp4',
            'title': 'Smoke Signals',
            'description': 'md5:929868d20021c924020641769eb3e7f1',
            'thumbnail': 'https://images.radio-canada.ca/v1/synps-cbc/episode/perso/cbc_schitts_creek_season_06e01_thumbnail_v01.jpg?im=Resize=(Size)',
            'duration': 1314,
            'categories': ['comedy'],
            'series': 'Schitt\'s Creek',
            'season': 'Season 6',
            'season_number': 6,
            'episode': 'Smoke Signals',
            'episode_number': 1,
            'episode_id': 'schitts-creek/s06e01',
        },
        'params': {'format': 'bv'},
        'skip': 'Geo-restricted to Canada',
    }, {
        # This video requires an account in the browser, but works fine in yt-dlp
        'url': 'https://gem.cbc.ca/media/schitts-creek/s01e01',
        'md5': '297a9600f554f2258aed01514226a697',
        'info_dict': {
            'id': 'schitts-creek/s01e01',
            'ext': 'mp4',
            'title': 'The Cup Runneth Over',
            'description': 'md5:9bca14ea49ab808097530eb05a29e797',
            'thumbnail': 'https://images.radio-canada.ca/v1/synps-cbc/episode/perso/cbc_schitts_creek_season_01e01_thumbnail_v01.jpg?im=Resize=(Size)',
            'series': 'Schitt\'s Creek',
            'season_number': 1,
            'season': 'Season 1',
            'episode_number': 1,
            'episode': 'The Cup Runneth Over',
            'episode_id': 'schitts-creek/s01e01',
            'duration': 1309,
            'categories': ['comedy'],
        },
        'params': {'format': 'bv'},
        'skip': 'Geo-restricted to Canada',
    }]

    _GEO_COUNTRIES = ['CA']
    _TOKEN_API_KEY = '3f4beddd-2061-49b0-ae80-6f1f2ed65b37'
    _NETRC_MACHINE = 'cbcgem'
    _claims_token = None

    def _new_claims_token(self, email, password):
        data = json.dumps({
            'email': email,
            'password': password,
        }).encode()
        headers = {'content-type': 'application/json'}
        query = {'apikey': self._TOKEN_API_KEY}
        resp = self._download_json('https://api.loginradius.com/identity/v2/auth/login',
                                   None, data=data, headers=headers, query=query)
        access_token = resp['access_token']

        query = {
            'access_token': access_token,
            'apikey': self._TOKEN_API_KEY,
            'jwtapp': 'jwt',
        }
        resp = self._download_json('https://cloud-api.loginradius.com/sso/jwt/api/token',
                                   None, headers=headers, query=query)
        sig = resp['signature']

        data = json.dumps({'jwt': sig}).encode()
        headers = {'content-type': 'application/json', 'ott-device-type': 'web'}
        resp = self._download_json('https://services.radio-canada.ca/ott/cbc-api/v2/token',
                                   None, data=data, headers=headers)
        cbc_access_token = resp['accessToken']

        headers = {'content-type': 'application/json', 'ott-device-type': 'web', 'ott-access-token': cbc_access_token}
        resp = self._download_json('https://services.radio-canada.ca/ott/cbc-api/v2/profile',
                                   None, headers=headers)
        return resp['claimsToken']

    def _get_claims_token_expiry(self):
        # Token is a JWT
        # JWT is decoded here and 'exp' field is extracted
        # It is a Unix timestamp for when the token expires
        b64_data = self._claims_token.split('.')[1]
        data = base64.urlsafe_b64decode(b64_data + "==")
        return json.loads(data)['exp']

    def claims_token_expired(self):
        exp = self._get_claims_token_expiry()
        if exp - time.time() < 10:
            # It will expire in less than 10 seconds, or has already expired
            return True
        return False

    def claims_token_valid(self):
        return self._claims_token is not None and not self.claims_token_expired()

    def _get_claims_token(self, email, password):
        if not self.claims_token_valid():
            self._claims_token = self._new_claims_token(email, password)
            self.cache.store(self._NETRC_MACHINE, 'claims_token', self._claims_token)
        return self._claims_token

    def _real_initialize(self):
        if self.claims_token_valid():
            return
        self._claims_token = self.cache.load(self._NETRC_MACHINE, 'claims_token')

    def _find_secret_formats(self, formats, video_id):
        """ Find a valid video url and convert it to the secret variant """
        base_format = next((f for f in formats if f.get('vcodec') != 'none'), None)
        if not base_format:
            return

        base_url = re.sub(r'(Manifest\(.*?),filter=[\w-]+(.*?\))', r'\1\2', base_format['url'])
        url = re.sub(r'(Manifest\(.*?),format=[\w-]+(.*?\))', r'\1\2', base_url)

        secret_xml = self._download_xml(url, video_id, note='Downloading secret XML', fatal=False)
        if not secret_xml:
            return

        for child in secret_xml:
            if child.attrib.get('Type') != 'video':
                continue
            for video_quality in child:
                bitrate = int_or_none(video_quality.attrib.get('Bitrate'))
                if not bitrate or 'Index' not in video_quality.attrib:
                    continue
                height = int_or_none(video_quality.attrib.get('MaxHeight'))

                yield {
                    **base_format,
                    'format_id': join_nonempty('sec', height),
                    # Note: \g<1> is necessary instead of \1 since bitrate is a number
                    'url': re.sub(r'(QualityLevels\()\d+(\))', fr'\g<1>{bitrate}\2', base_url),
                    'width': int_or_none(video_quality.attrib.get('MaxWidth')),
                    'tbr': bitrate / 1000.0,
                    'height': height,
                }

    def _real_extract(self, url):
        video_id = self._match_id(url)
        video_info = self._download_json('https://services.radio-canada.ca/ott/cbc-api/v2/assets/' + video_id, video_id)

        email, password = self._get_login_info()
        if email and password:
            claims_token = self._get_claims_token(email, password)
            headers = {'x-claims-token': claims_token}
        else:
            headers = {}
        m3u8_info = self._download_json(video_info['playSession']['url'], video_id, headers=headers)
        m3u8_url = m3u8_info.get('url')

        if m3u8_info.get('errorCode') == 1:
            self.raise_geo_restricted(countries=['CA'])
        elif m3u8_info.get('errorCode') == 35:
            self.raise_login_required(method='password')
        elif m3u8_info.get('errorCode') != 0:
            raise ExtractorError(f'{self.IE_NAME} said: {m3u8_info.get("errorCode")} - {m3u8_info.get("message")}')

        formats = self._extract_m3u8_formats(m3u8_url, video_id, m3u8_id='hls')
        self._remove_duplicate_formats(formats)
        formats.extend(self._find_secret_formats(formats, video_id))

        for format in formats:
            if format.get('vcodec') == 'none':
                if format.get('ext') is None:
                    format['ext'] = 'm4a'
                if format.get('acodec') is None:
                    format['acodec'] = 'mp4a.40.2'

                # Put described audio at the beginning of the list, so that it
                # isn't chosen by default, as most people won't want it.
                if 'descriptive' in format['format_id'].lower():
                    format['preference'] = -2

        self._sort_formats(formats)

        return {
            'id': video_id,
            'title': video_info['title'],
            'description': video_info.get('description'),
            'thumbnail': video_info.get('image'),
            'series': video_info.get('series'),
            'season_number': video_info.get('season'),
            'season': f'Season {video_info.get("season")}',
            'episode_number': video_info.get('episode'),
            'episode': video_info.get('title'),
            'episode_id': video_id,
            'duration': video_info.get('duration'),
            'categories': [video_info.get('category')],
            'formats': formats,
            'release_timestamp': video_info.get('airDate'),
            'timestamp': video_info.get('availableDate'),
        }

class CBCGemPlaylistIE(InfoExtractor):
    IE_NAME = 'gem.cbc.ca:playlist'
    _VALID_URL = r'https?://gem\.cbc\.ca/media/(?P<id>(?P<show>[0-9a-z-]+)/s(?P<season>[0-9]+))/?(?:[?#]|$)'
    _TESTS = [{
        # TV show playlist, all public videos
        'url': 'https://gem.cbc.ca/media/schitts-creek/s06',
        'playlist_count': 16,
        'info_dict': {
            'id': 'schitts-creek/s06',
            'title': 'Season 6',
            'description': 'md5:6a92104a56cbeb5818cc47884d4326a2',
        },
    }]
    _API_BASE = 'https://services.radio-canada.ca/ott/cbc-api/v2/shows/'

    def _real_extract(self, url):
        match = self._match_valid_url(url)
        season_id = match.group('id')
        show = match.group('show')
        show_info = self._download_json(self._API_BASE + show, season_id)
        season = int(match.group('season'))

        season_info = next((s for s in show_info['seasons'] if s.get('season') == season), None)

        if season_info is None:
            raise ExtractorError(f'Couldn\'t find season {season} of {show}')

        episodes = []
        for episode in season_info['assets']:
            episodes.append({
                '_type': 'url_transparent',
                'ie_key': 'CBCGem',
                'url': 'https://gem.cbc.ca/media/' + episode['id'],
                'id': episode['id'],
                'title': episode.get('title'),
                'description': episode.get('description'),
                'thumbnail': episode.get('image'),
                'series': episode.get('series'),
                'season_number': episode.get('season'),
                'season': season_info['title'],
                'season_id': season_info.get('id'),
                'episode_number': episode.get('episode'),
                'episode': episode.get('title'),
                'episode_id': episode['id'],
                'duration': episode.get('duration'),
                'categories': [episode.get('category')],
            })

        thumbnail = None
        tn_uri = season_info.get('image')
        # the-national was observed to use a "data:image/png;base64"
        # URI for their 'image' value. The image was 1x1, and is
        # probably just a placeholder, so it is ignored.
        if tn_uri is not None and not tn_uri.startswith('data:'):
            thumbnail = tn_uri

        return {
            '_type': 'playlist',
            'entries': episodes,
            'id': season_id,
            'title': season_info['title'],
            'description': season_info.get('description'),
            'thumbnail': thumbnail,
            'series': show_info.get('title'),
            'season_number': season_info.get('season'),
            'season': season_info['title'],
        }

class CBCGemLiveIE(InfoExtractor):
    IE_NAME = 'gem.cbc.ca:live'
    _VALID_URL = r'https?://gem\.cbc\.ca/live/(?P<id>\d+)'
    _TEST = {
        'url': 'https://gem.cbc.ca/live/920604739687',
        'info_dict': {
            'title': 'Ottawa',
            'description': 'The live TV channel and local programming from Ottawa',
            'thumbnail': 'https://thumbnails.cbc.ca/maven_legacy/thumbnails/CBC_OTT_VMS/Live_Channel_Static_Images/Ottawa_2880x1620.jpg',
            'is_live': True,
            'id': 'AyqZwxRqh8EH',
            'ext': 'mp4',
            'timestamp': 1492106160,
            'upload_date': '20170413',
            'uploader': 'CBCC-NEW',
        },
        'skip': 'Live might have ended',
    }

    # It's unclear where the chars at the end come from, but they appear to be
    # constant. Might need updating in the future.
    # There are two URLs, some livestreams are in one, and some
    # in the other. The JSON schema is the same for both.
    _API_URLS = ['https://tpfeed.cbc.ca/f/ExhSPC/t_t3UKJR6MAT', 'https://tpfeed.cbc.ca/f/ExhSPC/FNiv9xQx_BnT']

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

        for api_url in self._API_URLS:
            video_info = next((
                stream for stream in self._download_json(api_url, video_id)['entries']
                if stream.get('guid') == video_id), None)
            if video_info:
                break
        else:
            raise ExtractorError('Couldn\'t find video metadata, maybe this livestream is now offline', expected=True)

        return {
            '_type': 'url_transparent',
            'ie_key': 'ThePlatform',
            'url': video_info['content'][0]['url'],
            'id': video_id,
            'title': video_info.get('title'),
            'description': video_info.get('description'),
            'tags': try_get(video_info, lambda x: x['keywords'].split(', ')),
            'thumbnail': video_info.get('cbc$staticImage'),
            'is_live': True,
        }


code:
from .theplatform import ThePlatformFeedIE
from ..utils import (
    ExtractorError,
    int_or_none,
    find_xpath_attr,
    xpath_element,
    xpath_text,
    update_url_query,
    url_or_none,
)

class CBSBaseIE(ThePlatformFeedIE):
    def _parse_smil_subtitles(self, smil, namespace=None, subtitles_lang='en'):
        subtitles = {}
        for k, ext in [('sMPTE-TTCCURL', 'tt'), ('ClosedCaptionURL', 'ttml'), ('webVTTCaptionURL', 'vtt')]:
            cc_e = find_xpath_attr(smil, self._xpath_ns('.//param', namespace), 'name', k)
            if cc_e is not None:
                cc_url = cc_e.get('value')
                if cc_url:
                    subtitles.setdefault(subtitles_lang, []).append({
                        'ext': ext,
                        'url': cc_url,
                    })
        return subtitles

    def _extract_common_video_info(self, content_id, asset_types, mpx_acc, extra_info):
        tp_path = 'dJ5BDC/media/guid/%d/%s' % (mpx_acc, content_id)
        tp_release_url = f'https://link.theplatform.com/s/{tp_path}'
        info = self._extract_theplatform_metadata(tp_path, content_id)

        formats, subtitles = [], {}
        last_e = None
        for asset_type, query in asset_types.items():
            try:
                tp_formats, tp_subtitles = self._extract_theplatform_smil(
                    update_url_query(tp_release_url, query), content_id,
                    'Downloading %s SMIL data' % asset_type)
            except ExtractorError as e:
                last_e = e
                if asset_type != 'fallback':
                    continue
                query['formats'] = ''  # blank query to check if expired
                try:
                    tp_formats, tp_subtitles = self._extract_theplatform_smil(
                        update_url_query(tp_release_url, query), content_id,
                        'Downloading %s SMIL data, trying again with another format' % asset_type)
                except ExtractorError as e:
                    last_e = e
                    continue
            formats.extend(tp_formats)
            subtitles = self._merge_subtitles(subtitles, tp_subtitles)
        if last_e and not formats:
            self.raise_no_formats(last_e, True, content_id)
        self._sort_formats(formats)

        extra_info.update({
            'id': content_id,
            'formats': formats,
            'subtitles': subtitles,
        })
        info.update({k: v for k, v in extra_info.items() if v is not None})
        return info

    def _extract_video_info(self, *args, **kwargs):
        # Extract assets + metadata and call _extract_common_video_info
        raise NotImplementedError('This method must be implemented by subclasses')

    def _real_extract(self, url):
        return self._extract_video_info(self._match_id(url))

class CBSIE(CBSBaseIE):
    _VALID_URL = r'''(?x)
        (?:
            cbs:|
            https?://(?:www\.)?(?:
                cbs\.com/(?:shows|movies)/(?:video|[^/]+/video|[^/]+)/|
                colbertlateshow\.com/(?:video|podcasts)/)
        )(?P<id>[\w-]+)'''

    # All tests are blocked outside US
    _TESTS = [{
        'url': 'https://www.cbs.com/shows/video/xrUyNLtl9wd8D_RWWAg9NU2F_V6QpB3R/',
        'info_dict': {
            'id': 'xrUyNLtl9wd8D_RWWAg9NU2F_V6QpB3R',
            'ext': 'mp4',
            'title': 'Tough As Nails - Dreams Never Die',
            'description': 'md5:a3535a62531cdd52b0364248a2c1ae33',
            'duration': 2588,
            'timestamp': 1639015200,
            'upload_date': '20211209',
            'uploader': 'CBSI-NEW',
        },
        'params': {
            # m3u8 download
            'skip_download': True,
        },
    }, {
        'url': 'https://www.cbs.com/shows/video/sZH1MGgomIosZgxGJ1l263MFq16oMtW1/',
        'info_dict': {
            'id': 'sZH1MGgomIosZgxGJ1l263MFq16oMtW1',
            'title': 'The Late Show - 3/16/22 (Michael Buble, Rose Matafeo)',
            'timestamp': 1647488100,
            'description': 'md5:d0e6ec23c544b7fa8e39a8e6844d2439',
            'uploader': 'CBSI-NEW',
            'upload_date': '20220317',
        },
        'params': {
            'ignore_no_formats_error': True,
            'skip_download': True,
        },
        'expected_warnings': [
            'This content expired on', 'No video formats found', 'Requested format is not available'],
    }, {
        'url': 'http://colbertlateshow.com/video/8GmB0oY0McANFvp2aEffk9jZZZ2YyXxy/the-colbeard/',
        'only_matching': True,
    }, {
        'url': 'http://www.colbertlateshow.com/podcasts/dYSwjqPs_X1tvbV_P2FcPWRa_qT6akTC/in-the-bad-room-with-stephen/',
        'only_matching': True,
    }]

    def _extract_video_info(self, content_id, site='cbs', mpx_acc=2198311517):
        items_data = self._download_xml(
            'https://can.cbs.com/thunder/player/videoPlayerService.php',
            content_id, query={'partner': site, 'contentId': content_id})
        video_data = xpath_element(items_data, './/item')
        title = xpath_text(video_data, 'videoTitle', 'title') or xpath_text(video_data, 'videotitle', 'title')

        asset_types = {}
        has_drm = False
        for item in items_data.findall('.//item'):
            asset_type = xpath_text(item, 'assetType')
            query = {
                'mbr': 'true',
                'assetTypes': asset_type,
            }
            if not asset_type:
                # fallback for content_ids that videoPlayerService doesn't return anything for
                asset_type = 'fallback'
                query['formats'] = 'M3U+none,MPEG4,M3U+appleHlsEncryption,MP3'
                del query['assetTypes']
            if asset_type in asset_types:
                continue
            elif any(excluded in asset_type for excluded in ('HLS_FPS', 'DASH_CENC', 'OnceURL')):
                if 'DASH_CENC' in asset_type:
                    has_drm = True
                continue
            if asset_type.startswith('HLS') or 'StreamPack' in asset_type:
                query['formats'] = 'MPEG4,M3U'
            elif asset_type in ('RTMP', 'WIFI', '3G'):
                query['formats'] = 'MPEG4,FLV'
            asset_types[asset_type] = query

        if not asset_types and has_drm:
            self.report_drm(content_id)

        return self._extract_common_video_info(content_id, asset_types, mpx_acc, extra_info={
            'title': title,
            'series': xpath_text(video_data, 'seriesTitle'),
            'season_number': int_or_none(xpath_text(video_data, 'seasonNumber')),
            'episode_number': int_or_none(xpath_text(video_data, 'episodeNumber')),
            'duration': int_or_none(xpath_text(video_data, 'videoLength'), 1000),
            'thumbnail': url_or_none(xpath_text(video_data, 'previewImageURL')),
        })


code:
from .cbs import CBSIE
from ..utils import int_or_none

class CBSInteractiveIE(CBSIE):
    _VALID_URL = r'https?://(?:www\.)?(?P<site>cnet|zdnet)\.com/(?:videos|video(?:/share)?)/(?P<id>[^/?]+)'
    _TESTS = [{
        'url': 'http://www.cnet.com/videos/hands-on-with-microsofts-windows-8-1-update/',
        'info_dict': {
            'id': 'R49SYt__yAfmlXR85z4f7gNmCBDcN_00',
            'display_id': 'hands-on-with-microsofts-windows-8-1-update',
            'ext': 'mp4',
            'title': 'Hands-on with Microsoft Windows 8.1 Update',
            'description': 'The new update to the Windows 8 OS brings improved performance for mouse and keyboard users.',
            'uploader_id': '6085384d-619e-11e3-b231-14feb5ca9861',
            'uploader': 'Sarah Mitroff',
            'duration': 70,
            'timestamp': 1396479627,
            'upload_date': '20140402',
        },
        'params': {
            # m3u8 download
            'skip_download': True,
        },
    }, {
        'url': 'http://www.cnet.com/videos/whiny-pothole-tweets-at-local-government-when-hit-by-cars-tomorrow-daily-187/',
        'md5': 'f11d27b2fa18597fbf92444d2a9ed386',
        'info_dict': {
            'id': 'kjOJd_OoVJqbg_ZD8MZCOk8Wekb9QccK',
            'display_id': 'whiny-pothole-tweets-at-local-government-when-hit-by-cars-tomorrow-daily-187',
            'ext': 'mp4',
            'title': 'Whiny potholes tweet at local government when hit by cars (Tomorrow Daily 187)',
            'description': 'md5:d2b9a95a5ffe978ae6fbd4cf944d618f',
            'uploader_id': 'b163284d-6b73-44fc-b3e6-3da66c392d40',
            'uploader': 'Ashley Esqueda',
            'duration': 1482,
            'timestamp': 1433289889,
            'upload_date': '20150603',
        },
    }, {
        'url': 'http://www.zdnet.com/video/share/video-keeping-android-smartphones-and-tablets-secure/',
        'info_dict': {
            'id': 'k0r4T_ehht4xW_hAOqiVQPuBDPZ8SRjt',
            'display_id': 'video-keeping-android-smartphones-and-tablets-secure',
            'ext': 'mp4',
            'title': 'Video: Keeping Android smartphones and tablets secure',
            'description': 'Here\'s the best way to keep Android devices secure, and what you do when they\'ve come to the end of their lives.',
            'uploader_id': 'f2d97ea2-8175-11e2-9d12-0018fe8a00b0',
            'uploader': 'Adrian Kingsley-Hughes',
            'duration': 731,
            'timestamp': 1449129925,
            'upload_date': '20151203',
        },
        'params': {
            # m3u8 download
            'skip_download': True,
        },
    }, {
        'url': 'http://www.zdnet.com/video/huawei-matebook-x-video/',
        'only_matching': True,
    }]

    MPX_ACCOUNTS = {
        'cnet': 2198311517,
        'zdnet': 2387448114,
    }

    def _real_extract(self, url):
        site, display_id = self._match_valid_url(url).groups()
        webpage = self._download_webpage(url, display_id)

        data_json = self._html_search_regex(
            r"data(?:-(?:cnet|zdnet))?-video(?:-(?:uvp(?:js)?|player))?-options='([^']+)'",
            webpage, 'data json')
        data = self._parse_json(data_json, display_id)
        vdata = data.get('video') or (data.get('videos') or data.get('playlist'))[0]

        video_id = vdata['mpxRefId']

        title = vdata['title']
        author = vdata.get('author')
        if author:
            uploader = '%s %s' % (author['firstName'], author['lastName'])
            uploader_id = author.get('id')
        else:
            uploader = None
            uploader_id = None

        info = self._extract_video_info(video_id, site, self.MPX_ACCOUNTS[site])
        info.update({
            'id': video_id,
            'display_id': display_id,
            'title': title,
            'duration': int_or_none(vdata.get('duration')),
            'uploader': uploader,
            'uploader_id': uploader_id,
        })
        return info


code:
from .anvato import AnvatoIE
from .sendtonews import SendtoNewsIE
from ..compat import compat_urlparse
from ..utils import (
    parse_iso8601,
    unified_timestamp,
)

class CBSLocalIE(AnvatoIE):
    _VALID_URL_BASE = r'https?://[a-z]+\.cbslocal\.com/'
    _VALID_URL = _VALID_URL_BASE + r'video/(?P<id>\d+)'

    _TESTS = [{
        'url': 'http://newyork.cbslocal.com/video/3580809-a-very-blue-anniversary/',
        'info_dict': {
            'id': '3580809',
            'ext': 'mp4',
            'title': 'A Very Blue Anniversary',
            'description': 'CBS2’s Cindy Hsu has more.',
            'thumbnail': 're:^https?://.*',
            'timestamp': int,
            'upload_date': r're:^\d{8}$',
            'uploader': 'CBS',
            'subtitles': {
                'en': 'mincount:5',
            },
            'categories': [
                'Stations\\Spoken Word\\WCBSTV',
                'Syndication\\AOL',
                'Syndication\\MSN',
                'Syndication\\NDN',
                'Syndication\\Yahoo',
                'Content\\News',
                'Content\\News\\Local News',
            ],
            'tags': ['CBS 2 News Weekends', 'Cindy Hsu', 'Blue Man Group'],
        },
        'params': {
            'skip_download': True,
        },
    }]

    def _real_extract(self, url):
        mcp_id = self._match_id(url)
        return self.url_result(
            'anvato:anvato_cbslocal_app_web_prod_547f3e49241ef0e5d30c79b2efbca5d92c698f67:' + mcp_id, 'Anvato', mcp_id)

class CBSLocalArticleIE(AnvatoIE):
    _VALID_URL = CBSLocalIE._VALID_URL_BASE + r'\d+/\d+/\d+/(?P<id>[0-9a-z-]+)'

    _TESTS = [{
        # Anvato backend
        'url': 'http://losangeles.cbslocal.com/2016/05/16/safety-advocates-say-fatal-car-seat-failures-are-public-health-crisis',
        'md5': 'f0ee3081e3843f575fccef901199b212',
        'info_dict': {
            'id': '3401037',
            'ext': 'mp4',
            'title': 'Safety Advocates Say Fatal Car Seat Failures Are \'Public Health Crisis\'',
            'description': 'Collapsing seats have been the focus of scrutiny for decades, though experts say remarkably little has been done to address the issue. Randy Paige reports.',
            'thumbnail': 're:^https?://.*',
            'timestamp': 1463440500,
            'upload_date': '20160516',
            'uploader': 'CBS',
            'subtitles': {
                'en': 'mincount:5',
            },
            'categories': [
                'Stations\\Spoken Word\\KCBSTV',
                'Syndication\\MSN',
                'Syndication\\NDN',
                'Syndication\\AOL',
                'Syndication\\Yahoo',
                'Syndication\\Tribune',
                'Syndication\\Curb.tv',
                'Content\\News'
            ],
            'tags': ['CBS 2 News Evening'],
        },
    }, {
        # SendtoNews embed
        'url': 'http://cleveland.cbslocal.com/2016/05/16/indians-score-season-high-15-runs-in-blowout-win-over-reds-rapid-reaction/',
        'info_dict': {
            'id': 'GxfCe0Zo7D-175909-5588',
        },
        'playlist_count': 9,
        'params': {
            # m3u8 download
            'skip_download': True,
        },
    }]

    def _real_extract(self, url):
        display_id = self._match_id(url)
        webpage = self._download_webpage(url, display_id)

        sendtonews_url = SendtoNewsIE._extract_url(webpage)
        if sendtonews_url:
            return self.url_result(
                compat_urlparse.urljoin(url, sendtonews_url),
                ie=SendtoNewsIE.ie_key())

        info_dict = self._extract_anvato_videos(webpage, display_id)

        timestamp = unified_timestamp(self._html_search_regex(
            r'class="(?:entry|post)-date"[^>]*>([^<]+)', webpage,
            'released date', default=None)) or parse_iso8601(
            self._html_search_meta('uploadDate', webpage))

        info_dict.update({
            'display_id': display_id,
            'timestamp': timestamp,
        })

        return info_dict


code:
# from .cbs import CBSBaseIE
from .common import InfoExtractor
from ..utils import (
    int_or_none,
    try_get,
)

# class CBSSportsEmbedIE(CBSBaseIE):
class CBSSportsEmbedIE(InfoExtractor):
    IE_NAME = 'cbssports:embed'
    _VALID_URL = r'''(?ix)https?://(?:(?:www\.)?cbs|embed\.247)sports\.com/player/embed.+?
        (?:
            ids%3D(?P<id>[\da-f]{8}-(?:[\da-f]{4}-){3}[\da-f]{12})|
            pcid%3D(?P<pcid>\d+)
        )'''
    _TESTS = [{
        'url': 'https://www.cbssports.com/player/embed/?args=player_id%3Db56c03a6-231a-4bbe-9c55-af3c8a8e9636%26ids%3Db56c03a6-231a-4bbe-9c55-af3c8a8e9636%26resizable%3D1%26autoplay%3Dtrue%26domain%3Dcbssports.com%26comp_ads_enabled%3Dfalse%26watchAndRead%3D0%26startTime%3D0%26env%3Dprod',
        'only_matching': True,
    }, {
        'url': 'https://embed.247sports.com/player/embed/?args=%3fplayer_id%3d1827823171591%26channel%3dcollege-football-recruiting%26pcid%3d1827823171591%26width%3d640%26height%3d360%26autoplay%3dTrue%26comp_ads_enabled%3dFalse%26uvpc%3dhttps%253a%252f%252fwww.cbssports.com%252fapi%252fcontent%252fvideo%252fconfig%252f%253fcfg%253duvp_247sports_v4%2526partner%253d247%26uvpc_m%3dhttps%253a%252f%252fwww.cbssports.com%252fapi%252fcontent%252fvideo%252fconfig%252f%253fcfg%253duvp_247sports_m_v4%2526partner_m%253d247_mobile%26utag%3d247sportssite%26resizable%3dTrue',
        'only_matching': True,
    }]

    # def _extract_video_info(self, filter_query, video_id):
    #     return self._extract_feed_info('dJ5BDC', 'VxxJg8Ymh8sE', filter_query, video_id)

    def _real_extract(self, url):
        uuid, pcid = self._match_valid_url(url).groups()
        query = {'id': uuid} if uuid else {'pcid': pcid}
        video = self._download_json(
            'https://www.cbssports.com/api/content/video/',
            uuid or pcid, query=query)[0]
        video_id = video['id']
        title = video['title']
        metadata = video.get('metaData') or {}
        # return self._extract_video_info('byId=%d' % metadata['mpxOutletId'], video_id)
        # return self._extract_video_info('byGuid=' + metadata['mpxRefId'], video_id)

        formats = self._extract_m3u8_formats(
            metadata['files'][0]['url'], video_id, 'mp4',
            'm3u8_native', m3u8_id='hls', fatal=False)
        self._sort_formats(formats)

        image = video.get('image')
        thumbnails = None
        if image:
            image_path = image.get('path')
            if image_path:
                thumbnails = [{
                    'url': image_path,
                    'width': int_or_none(image.get('width')),
                    'height': int_or_none(image.get('height')),
                    'filesize': int_or_none(image.get('size')),
                }]

        return {
            'id': video_id,
            'title': title,
            'formats': formats,
            'thumbnails': thumbnails,
            'description': video.get('description'),
            'timestamp': int_or_none(try_get(video, lambda x: x['dateCreated']['epoch'])),
            'duration': int_or_none(metadata.get('duration')),
        }

class CBSSportsBaseIE(InfoExtractor):
    def _real_extract(self, url):
        display_id = self._match_id(url)
        webpage = self._download_webpage(url, display_id)
        iframe_url = self._search_regex(
            r'<iframe[^>]+(?:data-)?src="(https?://[^/]+/player/embed[^"]+)"',
            webpage, 'embed url')
        return self.url_result(iframe_url, CBSSportsEmbedIE.ie_key())

class CBSSportsIE(CBSSportsBaseIE):
    IE_NAME = 'cbssports'
    _VALID_URL = r'https?://(?:www\.)?cbssports\.com/[^/]+/video/(?P<id>[^/?#&]+)'
    _TESTS = [{
        'url': 'https://www.cbssports.com/college-football/video/cover-3-stanford-spring-gleaning/',
        'info_dict': {
            'id': 'b56c03a6-231a-4bbe-9c55-af3c8a8e9636',
            'ext': 'mp4',
            'title': 'Cover 3: Stanford Spring Gleaning',
            'description': 'The Cover 3 crew break down everything you need to know about the Stanford Cardinal this spring.',
            'timestamp': 1617218398,
            'upload_date': '20210331',
            'duration': 502,
        },
    }]

class TwentyFourSevenSportsIE(CBSSportsBaseIE):
    IE_NAME = '247sports'
    _VALID_URL = r'https?://(?:www\.)?247sports\.com/Video/(?:[^/?#&]+-)?(?P<id>\d+)'
    _TESTS = [{
        'url': 'https://247sports.com/Video/2021-QB-Jake-Garcia-senior-highlights-through-five-games-10084854/',
        'info_dict': {
            'id': '4f1265cb-c3b5-44a8-bb1d-1914119a0ccc',
            'ext': 'mp4',
            'title': '2021 QB Jake Garcia senior highlights through five games',
            'description': 'md5:8cb67ebed48e2e6adac1701e0ff6e45b',
            'timestamp': 1607114223,
            'upload_date': '20201204',
            'duration': 208,
        },
    }]


code:
from .common import InfoExtractor
from ..utils import (
    int_or_none,
    parse_iso8601,
    try_get,
    url_or_none,
)

class CCCIE(InfoExtractor):
    IE_NAME = 'media.ccc.de'
    _VALID_URL = r'https?://(?:www\.)?media\.ccc\.de/v/(?P<id>[^/?#&]+)'

    _TESTS = [{
        'url': 'https://media.ccc.de/v/30C3_-_5443_-_en_-_saal_g_-_201312281830_-_introduction_to_processor_design_-_byterazor#video',
        'md5': '3a1eda8f3a29515d27f5adb967d7e740',
        'info_dict': {
            'id': '1839',
            'ext': 'mp4',
            'title': 'Introduction to Processor Design',
            'creator': 'byterazor',
            'description': 'md5:df55f6d073d4ceae55aae6f2fd98a0ac',
            'thumbnail': r're:^https?://.*\.jpg$',
            'upload_date': '20131228',
            'timestamp': 1388188800,
            'duration': 3710,
            'tags': list,
        }
    }, {
        'url': 'https://media.ccc.de/v/32c3-7368-shopshifting#download',
        'only_matching': True,
    }]

    def _real_extract(self, url):
        display_id = self._match_id(url)
        webpage = self._download_webpage(url, display_id)
        event_id = self._search_regex(r"data-id='(\d+)'", webpage, 'event id')
        event_data = self._download_json('https://media.ccc.de/public/events/%s' % event_id, event_id)

        formats = []
        for recording in event_data.get('recordings', []):
            recording_url = recording.get('recording_url')
            if not recording_url:
                continue
            language = recording.get('language')
            folder = recording.get('folder')
            format_id = None
            if language:
                format_id = language
            if folder:
                if language:
                    format_id += '-' + folder
                else:
                    format_id = folder
            vcodec = 'h264' if 'h264' in folder else (
                'none' if folder in ('mp3', 'opus') else None
            )
            formats.append({
                'format_id': format_id,
                'url': recording_url,
                'width': int_or_none(recording.get('width')),
                'height': int_or_none(recording.get('height')),
                'filesize': int_or_none(recording.get('size'), invscale=1024 * 1024),
                'language': language,
                'vcodec': vcodec,
            })
        self._sort_formats(formats)

        return {
            'id': event_id,
            'display_id': display_id,
            'title': event_data['title'],
            'creator': try_get(event_data, lambda x: ', '.join(x['persons'])),
            'description': event_data.get('description'),
            'thumbnail': event_data.get('thumb_url'),
            'timestamp': parse_iso8601(event_data.get('date')),
            'duration': int_or_none(event_data.get('length')),
            'view_count': int_or_none(event_data.get('view_count')),
            'tags': event_data.get('tags'),
            'formats': formats,
        }

class CCCPlaylistIE(InfoExtractor):
    IE_NAME = 'media.ccc.de:lists'
    _VALID_URL = r'https?://(?:www\.)?media\.ccc\.de/c/(?P<id>[^/?#&]+)'
    _TESTS = [{
        'url': 'https://media.ccc.de/c/30c3',
        'info_dict': {
            'title': '30C3',
            'id': '30c3',
        },
        'playlist_count': 135,
    }]

    def _real_extract(self, url):
        playlist_id = self._match_id(url).lower()

        conf = self._download_json(
            'https://media.ccc.de/public/conferences/' + playlist_id,
            playlist_id)

        entries = []
        for e in conf['events']:
            event_url = url_or_none(e.get('frontend_link'))
            if event_url:
                entries.append(self.url_result(event_url, ie=CCCIE.ie_key()))

        return self.playlist_result(entries, playlist_id, conf.get('title'))


code:
from .common import InfoExtractor
from ..utils import (
    clean_html,
    int_or_none,
    parse_duration,
    parse_resolution,
    try_get,
    unified_timestamp,
    url_or_none,
)

class CCMAIE(InfoExtractor):
    _VALID_URL = r'https?://(?:www\.)?ccma\.cat/(?:[^/]+/)*?(?P<type>video|audio)/(?P<id>\d+)'
    _TESTS = [{
        'url': 'http://www.ccma.cat/tv3/alacarta/lespot-de-la-marato-de-tv3/lespot-de-la-marato-de-tv3/video/5630208/',
        'md5': '7296ca43977c8ea4469e719c609b0871',
        'info_dict': {
            'id': '5630208',
            'ext': 'mp4',
            'title': 'L\'espot de La Marató de TV3',
            'description': 'md5:f12987f320e2f6e988e9908e4fe97765',
            'timestamp': 1478608140,
            'upload_date': '20161108',
            'age_limit': 0,
        }
    }, {
        'url': 'http://www.ccma.cat/catradio/alacarta/programa/el-consell-de-savis-analitza-el-derbi/audio/943685/',
        'md5': 'fa3e38f269329a278271276330261425',
        'info_dict': {
            'id': '943685',
            'ext': 'mp3',
            'title': 'El Consell de Savis analitza el derbi',
            'description': 'md5:e2a3648145f3241cb9c6b4b624033e53',
            'upload_date': '20170512',
            'timestamp': 1494622500,
            'vcodec': 'none',
            'categories': ['Esports'],
        }
    }, {
        'url': 'http://www.ccma.cat/tv3/alacarta/crims/crims-josep-tallada-lespereu-me-capitol-1/video/6031387/',
        'md5': 'b43c3d3486f430f3032b5b160d80cbc3',
        'info_dict': {
            'id': '6031387',
            'ext': 'mp4',
            'title': 'Crims - Josep Talleda, l\'"Espereu-me" (capítol 1)',
            'description': 'md5:7cbdafb640da9d0d2c0f62bad1e74e60',
            'timestamp': 1582577700,
            'upload_date': '20200224',
            'subtitles': 'mincount:4',
            'age_limit': 16,
            'series': 'Crims',
        }
    }]

    def _real_extract(self, url):
        media_type, media_id = self._match_valid_url(url).groups()

        media = self._download_json(
            'http://dinamics.ccma.cat/pvideo/media.jsp', media_id, query={
                'media': media_type,
                'idint': media_id,
            })

        formats = []
        media_url = media['media']['url']
        if isinstance(media_url, list):
            for format_ in media_url:
                format_url = url_or_none(format_.get('file'))
                if not format_url:
                    continue
                label = format_.get('label')
                f = parse_resolution(label)
                f.update({
                    'url': format_url,
                    'format_id': label,
                })
                formats.append(f)
        else:
            formats.append({
                'url': media_url,
                'vcodec': 'none' if media_type == 'audio' else None,
            })
        self._sort_formats(formats)

        informacio = media['informacio']
        title = informacio['titol']
        durada = informacio.get('durada') or {}
        duration = int_or_none(durada.get('milisegons'), 1000) or parse_duration(durada.get('text'))
        tematica = try_get(informacio, lambda x: x['tematica']['text'])

        data_utc = try_get(informacio, lambda x: x['data_emissio']['utc'])
        timestamp = unified_timestamp(data_utc)

        subtitles = {}
        subtitols = media.get('subtitols') or []
        if isinstance(subtitols, dict):
            subtitols = [subtitols]
        for st in subtitols:
            sub_url = st.get('url')
            if sub_url:
                subtitles.setdefault(
                    st.get('iso') or st.get('text') or 'ca', []).append({
                        'url': sub_url,
                    })

        thumbnails = []
        imatges = media.get('imatges', {})
        if imatges:
            thumbnail_url = imatges.get('url')
            if thumbnail_url:
                thumbnails = [{
                    'url': thumbnail_url,
                    'width': int_or_none(imatges.get('amplada')),
                    'height': int_or_none(imatges.get('alcada')),
                }]

        age_limit = None
        codi_etic = try_get(informacio, lambda x: x['codi_etic']['id'])
        if codi_etic:
            codi_etic_s = codi_etic.split('_')
            if len(codi_etic_s) == 2:
                if codi_etic_s[1] == 'TP':
                    age_limit = 0
                else:
                    age_limit = int_or_none(codi_etic_s[1])

        return {
            'id': media_id,
            'title': title,
            'description': clean_html(informacio.get('descripcio')),
            'duration': duration,
            'timestamp': timestamp,
            'thumbnails': thumbnails,
            'subtitles': subtitles,
            'formats': formats,
            'age_limit': age_limit,
            'alt_title': informacio.get('titol_complet'),
            'episode_number': int_or_none(informacio.get('capitol')),
            'categories': [tematica] if tematica else None,
            'series': informacio.get('programa'),
        }


code:
import re

from .common import InfoExtractor
from ..compat import compat_str
from ..utils import (
    float_or_none,
    try_get,
    unified_timestamp,
)

class CCTVIE(InfoExtractor):
    IE_DESC = '央视网'
    _VALID_URL = r'https?://(?:(?:[^/]+)\.(?:cntv|cctv)\.(?:com|cn)|(?:www\.)?ncpa-classic\.com)/(?:[^/]+/)*?(?P<id>[^/?#&]+?)(?:/index)?(?:\.s?html|[?#&]|$)'
    _TESTS = [{
        # fo.addVariable("videoCenterId","id")
        'url': 'http://sports.cntv.cn/2016/02/12/ARTIaBRxv4rTT1yWf1frW2wi160212.shtml',
        'md5': 'd61ec00a493e09da810bf406a078f691',
        'info_dict': {
            'id': '5ecdbeab623f4973b40ff25f18b174e8',
            'ext': 'mp4',
            'title': '[NBA]二少联手砍下46分 雷霆主场击败鹈鹕（快讯）',
            'description': 'md5:7e14a5328dc5eb3d1cd6afbbe0574e95',
            'duration': 98,
            'uploader': 'songjunjie',
            'timestamp': 1455279956,
            'upload_date': '20160212',
        },
    }, {
        # var guid = "id"
        'url': 'http://tv.cctv.com/2016/02/05/VIDEUS7apq3lKrHG9Dncm03B160205.shtml',
        'info_dict': {
            'id': 'efc5d49e5b3b4ab2b34f3a502b73d3ae',
            'ext': 'mp4',
            'title': '[赛车]“车王”舒马赫恢复情况成谜（快讯）',
            'description': '2月4日，蒙特泽莫罗透露了关于“车王”舒马赫恢复情况，但情况是否属实遭到了质疑。',
            'duration': 37,
            'uploader': 'shujun',
            'timestamp': 1454677291,
            'upload_date': '20160205',
        },
        'params': {
            'skip_download': True,
        },
    }, {
        # changePlayer('id')
        'url': 'http://english.cntv.cn/special/four_comprehensives/index.shtml',
        'info_dict': {
            'id': '4bb9bb4db7a6471ba85fdeda5af0381e',
            'ext': 'mp4',
            'title': 'NHnews008 ANNUAL POLITICAL SEASON',
            'description': 'Four Comprehensives',
            'duration': 60,
            'uploader': 'zhangyunlei',
            'timestamp': 1425385521,
            'upload_date': '20150303',
        },
        'params': {
            'skip_download': True,
        },
    }, {
        # loadvideo('id')
        'url': 'http://cctv.cntv.cn/lm/tvseries_russian/yilugesanghua/index.shtml',
        'info_dict': {
            'id': 'b15f009ff45c43968b9af583fc2e04b2',
            'ext': 'mp4',
            'title': 'Путь，усыпанный космеями Серия 1',
            'description': 'Путь, усыпанный космеями',
            'duration': 2645,
            'uploader': 'renxue',
            'timestamp': 1477479241,
            'upload_date': '20161026',
        },
        'params': {
            'skip_download': True,
        },
    }, {
        # var initMyAray = 'id'
        'url': 'http://www.ncpa-classic.com/2013/05/22/VIDE1369219508996867.shtml',
        'info_dict': {
            'id': 'a194cfa7f18c426b823d876668325946',
            'ext': 'mp4',
            'title': '小泽征尔音乐塾 音乐梦想无国界',
            'duration': 2173,
            'timestamp': 1369248264,
            'upload_date': '20130522',
        },
        'params': {
            'skip_download': True,
        },
    }, {
        # var ids = ["id"]
        'url': 'http://www.ncpa-classic.com/clt/more/416/index.shtml',
        'info_dict': {
            'id': 'a8606119a4884588a79d81c02abecc16',
            'ext': 'mp3',
            'title': '来自维也纳的新年贺礼',
            'description': 'md5:f13764ae8dd484e84dd4b39d5bcba2a7',
            'duration': 1578,
            'uploader': 'djy',
            'timestamp': 1482942419,
            'upload_date': '20161228',
        },
        'params': {
            'skip_download': True,
        },
        'expected_warnings': ['Failed to download m3u8 information'],
    }, {
        'url': 'http://ent.cntv.cn/2016/01/18/ARTIjprSSJH8DryTVr5Bx8Wb160118.shtml',
        'only_matching': True,
    }, {
        'url': 'http://tv.cntv.cn/video/C39296/e0210d949f113ddfb38d31f00a4e5c44',
        'only_matching': True,
    }, {
        'url': 'http://english.cntv.cn/2016/09/03/VIDEhnkB5y9AgHyIEVphCEz1160903.shtml',
        'only_matching': True,
    }, {
        'url': 'http://tv.cctv.com/2016/09/07/VIDE5C1FnlX5bUywlrjhxXOV160907.shtml',
        'only_matching': True,
    }, {
        'url': 'http://tv.cntv.cn/video/C39296/95cfac44cabd3ddc4a9438780a4e5c44',
        'only_matching': True,
    }]

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

        video_id = self._search_regex(
            [r'var\s+guid\s*=\s*["\']([\da-fA-F]+)',
             r'videoCenterId["\']\s*,\s*["\']([\da-fA-F]+)',
             r'changePlayer\s*\(\s*["\']([\da-fA-F]+)',
             r'load[Vv]ideo\s*\(\s*["\']([\da-fA-F]+)',
             r'var\s+initMyAray\s*=\s*["\']([\da-fA-F]+)',
             r'var\s+ids\s*=\s*\[["\']([\da-fA-F]+)'],
            webpage, 'video id')

        data = self._download_json(
            'http://vdn.apps.cntv.cn/api/getHttpVideoInfo.do', video_id,
            query={
                'pid': video_id,
                'url': url,
                'idl': 32,
                'idlr': 32,
                'modifyed': 'false',
            })

        title = data['title']

        formats = []

        video = data.get('video')
        if isinstance(video, dict):
            for quality, chapters_key in enumerate(('lowChapters', 'chapters')):
                video_url = try_get(
                    video, lambda x: x[chapters_key][0]['url'], compat_str)
                if video_url:
                    formats.append({
                        'url': video_url,
                        'format_id': 'http',
                        'quality': quality,
                        # Sample clip
                        'preference': -10
                    })

        hls_url = try_get(data, lambda x: x['hls_url'], compat_str)
        if hls_url:
            hls_url = re.sub(r'maxbr=\d+&?', '', hls_url)
            formats.extend(self._extract_m3u8_formats(
                hls_url, video_id, 'mp4', entry_protocol='m3u8_native',
                m3u8_id='hls', fatal=False))

        self._sort_formats(formats)

        uploader = data.get('editer_name')
        description = self._html_search_meta(
            'description', webpage, default=None)
        timestamp = unified_timestamp(data.get('f_pgmtime'))
        duration = float_or_none(try_get(video, lambda x: x['totalLength']))

        return {
            'id': video_id,
            'title': title,
            'description': description,
            'uploader': uploader,
            'timestamp': timestamp,
            'duration': duration,
            'formats': formats,
        }


code:
import codecs
import json
import re

from .common import InfoExtractor
from ..compat import compat_ord, compat_urllib_parse_unquote
from ..utils import (
    ExtractorError,
    float_or_none,
    int_or_none,
    merge_dicts,
    multipart_encode,
    parse_duration,
    random_birthday,
    try_get,
    urljoin,
)

class CDAIE(InfoExtractor):
    _VALID_URL = r'https?://(?:(?:www\.)?cda\.pl/video|ebd\.cda\.pl/[0-9]+x[0-9]+)/(?P<id>[0-9a-z]+)'
    _BASE_URL = 'http://www.cda.pl/'
    _TESTS = [{
        'url': 'http://www.cda.pl/video/5749950c',
        'md5': '6f844bf51b15f31fae165365707ae970',
        'info_dict': {
            'id': '5749950c',
            'ext': 'mp4',
            'height': 720,
            'title': 'Oto dlaczego przed zakrętem należy zwolnić.',
            'description': 'md5:269ccd135d550da90d1662651fcb9772',
            'thumbnail': r're:^https?://.*\.jpg$',
            'average_rating': float,
            'duration': 39,
            'age_limit': 0,
            'upload_date': '20160221',
            'timestamp': 1456078244,
        }
    }, {
        'url': 'http://www.cda.pl/video/57413289',
        'md5': 'a88828770a8310fc00be6c95faf7f4d5',
        'info_dict': {
            'id': '57413289',
            'ext': 'mp4',
            'title': 'Lądowanie na lotnisku na Maderze',
            'description': 'md5:60d76b71186dcce4e0ba6d4bbdb13e1a',
            'thumbnail': r're:^https?://.*\.jpg$',
            'uploader': 'crash404',
            'view_count': int,
            'average_rating': float,
            'duration': 137,
            'age_limit': 0,
        }
    }, {
        # Age-restricted
        'url': 'http://www.cda.pl/video/1273454c4',
        'info_dict': {
            'id': '1273454c4',
            'ext': 'mp4',
            'title': 'Bronson (2008) napisy HD 1080p',
            'description': 'md5:1b6cb18508daf2dc4e0fa4db77fec24c',
            'height': 1080,
            'uploader': 'boniek61',
            'thumbnail': r're:^https?://.*\.jpg$',
            'duration': 5554,
            'age_limit': 18,
            'view_count': int,
            'average_rating': float,
        },
    }, {
        'url': 'http://ebd.cda.pl/0x0/5749950c',
        'only_matching': True,
    }]

    def _download_age_confirm_page(self, url, video_id, *args, **kwargs):
        form_data = random_birthday('rok', 'miesiac', 'dzien')
        form_data.update({'return': url, 'module': 'video', 'module_id': video_id})
        data, content_type = multipart_encode(form_data)
        return self._download_webpage(
            urljoin(url, '/a/validatebirth'), video_id, *args,
            data=data, headers={
                'Referer': url,
                'Content-Type': content_type,
            }, **kwargs)

    def _real_extract(self, url):
        video_id = self._match_id(url)
        self._set_cookie('cda.pl', 'cda.player', 'html5')
        webpage = self._download_webpage(
            self._BASE_URL + '/video/' + video_id, video_id)

        if 'Ten film jest dostępny dla użytkowników premium' in webpage:
            raise ExtractorError('This video is only available for premium users.', expected=True)

        if re.search(r'niedostępn[ey] w(?: |\s+)Twoim kraju\s*<', webpage):
            self.raise_geo_restricted()

        need_confirm_age = False
        if self._html_search_regex(r'(<form[^>]+action="[^"]*/a/validatebirth[^"]*")',
                                   webpage, 'birthday validate form', default=None):
            webpage = self._download_age_confirm_page(
                url, video_id, note='Confirming age')
            need_confirm_age = True

        formats = []

        uploader = self._search_regex(r'''(?x)
            <(span|meta)[^>]+itemprop=(["\'])author\2[^>]*>
            (?:<\1[^>]*>[^<]*</\1>|(?!</\1>)(?:.|\n))*?
            <(span|meta)[^>]+itemprop=(["\'])name\4[^>]*>(?P<uploader>[^<]+)</\3>
        ''', webpage, 'uploader', default=None, group='uploader')
        view_count = self._search_regex(
            r'Odsłony:(?:\s| )*([0-9]+)', webpage,
            'view_count', default=None)
        average_rating = self._search_regex(
            (r'<(?:span|meta)[^>]+itemprop=(["\'])ratingValue\1[^>]*>(?P<rating_value>[0-9.]+)',
             r'<span[^>]+\bclass=["\']rating["\'][^>]*>(?P<rating_value>[0-9.]+)'), webpage, 'rating', fatal=False,
            group='rating_value')

        info_dict = {
            'id': video_id,
            'title': self._og_search_title(webpage),
            'description': self._og_search_description(webpage),
            'uploader': uploader,
            'view_count': int_or_none(view_count),
            'average_rating': float_or_none(average_rating),
            'thumbnail': self._og_search_thumbnail(webpage),
            'formats': formats,
            'duration': None,
            'age_limit': 18 if need_confirm_age else 0,
        }

        info = self._search_json_ld(webpage, video_id, default={})

        # Source: https://www.cda.pl/js/player.js?t=1606154898
        def decrypt_file(a):
            for p in ('_XDDD', '_CDA', '_ADC', '_CXD', '_QWE', '_Q5', '_IKSDE'):
                a = a.replace(p, '')
            a = compat_urllib_parse_unquote(a)
            b = []
            for c in a:
                f = compat_ord(c)
                b.append(chr(33 + (f + 14) % 94) if 33 <= f <= 126 else chr(f))
            a = ''.join(b)
            a = a.replace('.cda.mp4', '')
            for p in ('.2cda.pl', '.3cda.pl'):
                a = a.replace(p, '.cda.pl')
            if '/upstream' in a:
                a = a.replace('/upstream', '.mp4/upstream')
                return 'https://' + a
            return 'https://' + a + '.mp4'

        def extract_format(page, version):
            json_str = self._html_search_regex(
                r'player_data=(\\?["\'])(?P<player_data>.+?)\1', page,
                '%s player_json' % version, fatal=False, group='player_data')
            if not json_str:
                return
            player_data = self._parse_json(
                json_str, '%s player_data' % version, fatal=False)
            if not player_data:
                return
            video = player_data.get('video')
            if not video or 'file' not in video:
                self.report_warning('Unable to extract %s version information' % version)
                return
            if video['file'].startswith('uggc'):
                video['file'] = codecs.decode(video['file'], 'rot_13')
                if video['file'].endswith('adc.mp4'):
                    video['file'] = video['file'].replace('adc.mp4', '.mp4')
            elif not video['file'].startswith('http'):
                video['file'] = decrypt_file(video['file'])
            video_quality = video.get('quality')
            qualities = video.get('qualities', {})
            video_quality = next((k for k, v in qualities.items() if v == video_quality), video_quality)
            info_dict['formats'].append({
                'url': video['file'],
                'format_id': video_quality,
                'height': int_or_none(video_quality[:-1]),
            })
            for quality, cda_quality in qualities.items():
                if quality == video_quality:
                    continue
                data = {'jsonrpc': '2.0', 'method': 'videoGetLink', 'id': 2,
                        'params': [video_id, cda_quality, video.get('ts'), video.get('hash2'), {}]}
                data = json.dumps(data).encode('utf-8')
                video_url = self._download_json(
                    f'https://www.cda.pl/video/{video_id}', video_id, headers={
                        'Content-Type': 'application/json',
                        'X-Requested-With': 'XMLHttpRequest'
                    }, data=data, note=f'Fetching {quality} url',
                    errnote=f'Failed to fetch {quality} url', fatal=False)
                if try_get(video_url, lambda x: x['result']['status']) == 'ok':
                    video_url = try_get(video_url, lambda x: x['result']['resp'])
                    info_dict['formats'].append({
                        'url': video_url,
                        'format_id': quality,
                        'height': int_or_none(quality[:-1])
                    })

            if not info_dict['duration']:
                info_dict['duration'] = parse_duration(video.get('duration'))

        extract_format(webpage, 'default')

        for href, resolution in re.findall(
                r'<a[^>]+data-quality="[^"]+"[^>]+href="([^"]+)"[^>]+class="quality-btn"[^>]*>([0-9]+p)',
                webpage):
            if need_confirm_age:
                handler = self._download_age_confirm_page
            else:
                handler = self._download_webpage

            webpage = handler(
                urljoin(self._BASE_URL, href), video_id,
                'Downloading %s version information' % resolution, fatal=False)
            if not webpage:
                # Manually report warning because empty page is returned when
                # invalid version is requested.
                self.report_warning('Unable to download %s version information' % resolution)
                continue

            extract_format(webpage, resolution)

        self._sort_formats(formats)

        return merge_dicts(info_dict, info)


code:
from .common import InfoExtractor
from ..utils import traverse_obj

class CellebriteIE(InfoExtractor):
    _VALID_URL = r'https?://cellebrite\.com/(?:\w+)?/(?P<id>[\w-]+)'
    _TESTS = [{
        'url': 'https://cellebrite.com/en/collect-data-from-android-devices-with-cellebrite-ufed/',
        'info_dict': {
            'id': '16025876',
            'ext': 'mp4',
            'description': 'md5:174571cb97083fd1d457d75c684f4e2b',
            'thumbnail': 'https://cellebrite.com/wp-content/uploads/2021/05/Chat-Capture-1024x559.png',
            'title': 'Ask the Expert: Chat Capture - Collect Data from Android Devices in Cellebrite UFED',
            'duration': 455,
            'tags': [],
        }
    }, {
        'url': 'https://cellebrite.com/en/how-to-lawfully-collect-the-maximum-amount-of-data-from-android-devices/',
        'info_dict': {
            'id': '29018255',
            'ext': 'mp4',
            'duration': 134,
            'tags': [],
            'description': 'md5:e9a3d124c7287b0b07bad2547061cacf',
            'thumbnail': 'https://cellebrite.com/wp-content/uploads/2022/07/How-to-Lawfully-Collect-the-Maximum-Amount-of-Data-From-Android-Devices.png',
            'title': 'Android Extractions Explained',
        }
    }]

    def _get_formats_and_subtitles(self, json_data, display_id):
        formats = [{'url': url} for url in traverse_obj(json_data, ('mp4', ..., 'url')) or []]
        subtitles = {}

        for url in traverse_obj(json_data, ('hls', ..., 'url')) or []:
            fmt, sub = self._extract_m3u8_formats_and_subtitles(
                url, display_id, ext='mp4', headers={'Referer': 'https://play.vidyard.com/'})
            formats.extend(fmt)
            self._merge_subtitles(sub, target=subtitles)

        return formats, subtitles

    def _real_extract(self, url):
        display_id = self._match_id(url)
        webpage = self._download_webpage(url, display_id)

        player_uuid = self._search_regex(
            r'<img\s[^>]*\bdata-uuid\s*=\s*"([^"\?]+)', webpage, 'player UUID')
        json_data = self._download_json(
            f'https://play.vidyard.com/player/{player_uuid}.json', display_id)['payload']['chapters'][0]

        formats, subtitles = self._get_formats_and_subtitles(json_data['sources'], display_id)
        self._sort_formats(formats)
        return {
            'id': str(json_data['videoId']),
            'title': json_data.get('name') or self._og_search_title(webpage),
            'formats': formats,
            'subtitles': subtitles,
            'description': json_data.get('description') or self._og_search_description(webpage),
            'duration': json_data.get('seconds'),
            'tags': json_data.get('tags'),
            'thumbnail': self._og_search_thumbnail(webpage),
            'http_headers': {'Referer': 'https://play.vidyard.com/'},
        }