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

class UMGDeIE(InfoExtractor):
    IE_NAME = 'umg:de'
    IE_DESC = 'Universal Music Deutschland'
    _VALID_URL = r'https?://(?:www\.)?universal-music\.de/[^/]+/videos/[^/?#]+-(?P<id>\d+)'
    _TEST = {
        'url': 'https://www.universal-music.de/sido/videos/jedes-wort-ist-gold-wert-457803',
        'md5': 'ebd90f48c80dcc82f77251eb1902634f',
        'info_dict': {
            'id': '457803',
            'ext': 'mp4',
            'title': 'Jedes Wort ist Gold wert',
            'timestamp': 1513591800,
            'upload_date': '20171218',
        }
    }

    def _real_extract(self, url):
        video_id = self._match_id(url)
        video_data = self._download_json(
            'https://graphql.universal-music.de/',
            video_id, query={
                'query': '''{
  universalMusic(channel:16) {
    video(id:%s) {
      headline
      formats {
        formatId
        url
        type
        width
        height
        mimeType
        fileSize
      }
      duration
      createdDate
    }
  }
}''' % video_id})['data']['universalMusic']['video']

        title = video_data['headline']
        hls_url_template = 'http://mediadelivery.universal-music-services.de/vod/mp4:autofill/storage/' + '/'.join(list(video_id)) + '/content/%s/file/playlist.m3u8'

        thumbnails = []
        formats = []

        def add_m3u8_format(format_id):
            formats.extend(self._extract_m3u8_formats(
                hls_url_template % format_id, video_id, 'mp4',
                'm3u8_native', m3u8_id='hls', fatal=False))

        for f in video_data.get('formats', []):
            f_url = f.get('url')
            mime_type = f.get('mimeType')
            if not f_url or mime_type == 'application/mxf':
                continue
            fmt = {
                'url': f_url,
                'width': int_or_none(f.get('width')),
                'height': int_or_none(f.get('height')),
                'filesize': parse_filesize(f.get('fileSize')),
            }
            f_type = f.get('type')
            if f_type == 'Image':
                thumbnails.append(fmt)
            elif f_type == 'Video':
                format_id = f.get('formatId')
                if format_id:
                    fmt['format_id'] = format_id
                    if mime_type == 'video/mp4':
                        add_m3u8_format(format_id)
                urlh = self._request_webpage(f_url, video_id, fatal=False)
                if urlh:
                    first_byte = urlh.read(1)
                    if first_byte not in (b'F', b'\x00'):
                        continue
                    formats.append(fmt)
        if not formats:
            for format_id in (867, 836, 940):
                add_m3u8_format(format_id)
        self._sort_formats(formats)

        return {
            'id': video_id,
            'title': title,
            'duration': int_or_none(video_data.get('duration')),
            'timestamp': parse_iso8601(video_data.get('createdDate'), ' '),
            'thumbnails': thumbnails,
            'formats': formats,
        }


code:
import re

from .common import InfoExtractor
from ..utils import qualities

class UnistraIE(InfoExtractor):
    _VALID_URL = r'https?://utv\.unistra\.fr/(?:index|video)\.php\?id_video\=(?P<id>\d+)'

    _TESTS = [
        {
            'url': 'http://utv.unistra.fr/video.php?id_video=154',
            'md5': '736f605cfdc96724d55bb543ab3ced24',
            'info_dict': {
                'id': '154',
                'ext': 'mp4',
                'title': 'M!ss Yella',
                'description': 'md5:104892c71bd48e55d70b902736b81bbf',
            },
        },
        {
            'url': 'http://utv.unistra.fr/index.php?id_video=437',
            'md5': '1ddddd6cccaae76f622ce29b8779636d',
            'info_dict': {
                'id': '437',
                'ext': 'mp4',
                'title': 'Prix Louise Weiss 2014',
                'description': 'md5:cc3a8735f079f4fb6b0b570fc10c135a',
            },
        }
    ]

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

        webpage = self._download_webpage(url, video_id)

        files = set(re.findall(r'file\s*:\s*"(/[^"]+)"', webpage))

        quality = qualities(['SD', 'HD'])
        formats = []
        for file_path in files:
            format_id = 'HD' if file_path.endswith('-HD.mp4') else 'SD'
            formats.append({
                'url': 'http://vod-flash.u-strasbg.fr:8080%s' % file_path,
                'format_id': format_id,
                'quality': quality(format_id)
            })
        self._sort_formats(formats)

        title = self._html_search_regex(
            r'<title>UTV - (.*?)</', webpage, 'title')
        description = self._html_search_regex(
            r'<meta name="Description" content="(.*?)"', webpage, 'description', flags=re.DOTALL)
        thumbnail = self._search_regex(
            r'image: "(.*?)"', webpage, 'thumbnail')

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

repo_name: Lesmiscore/ytdl


code:
from .common import InfoExtractor
from .youtube import YoutubeIE

class UnityIE(InfoExtractor):
    _VALID_URL = r'https?://(?:www\.)?unity3d\.com/learn/tutorials/(?:[^/]+/)*(?P<id>[^/?#&]+)'
    _TESTS = [{
        'url': 'https://unity3d.com/learn/tutorials/topics/animation/animate-anything-mecanim',
        'info_dict': {
            'id': 'jWuNtik0C8E',
            'ext': 'mp4',
            'title': 'Live Training 22nd September 2014 -  Animate Anything',
            'description': 'md5:e54913114bd45a554c56cdde7669636e',
            'duration': 2893,
            'uploader': 'Unity',
            'uploader_id': 'Unity3D',
            'upload_date': '20140926',
        }
    }, {
        'url': 'https://unity3d.com/learn/tutorials/projects/2d-ufo-tutorial/following-player-camera?playlist=25844',
        'only_matching': True,
    }]

    def _real_extract(self, url):
        video_id = self._match_id(url)
        webpage = self._download_webpage(url, video_id)
        youtube_id = self._search_regex(
            r'data-video-id="([_0-9a-zA-Z-]+)"',
            webpage, 'youtube ID')
        return self.url_result(youtube_id, ie=YoutubeIE.ie_key(), video_id=video_id)


code:
from .common import InfoExtractor
from ..compat import (
    compat_str,
    compat_urllib_parse_urlencode,
)
from ..utils import (
    clean_html,
    int_or_none,
    parse_duration,
    parse_iso8601,
    qualities,
    update_url_query,
)

class UOLIE(InfoExtractor):
    IE_NAME = 'uol.com.br'
    _VALID_URL = r'https?://(?:.+?\.)?uol\.com\.br/.*?(?:(?:mediaId|v)=|view/(?:[a-z0-9]+/)?|video(?:=|/(?:\d{4}/\d{2}/\d{2}/)?))(?P<id>\d+|[\w-]+-[A-Z0-9]+)'
    _TESTS = [{
        'url': 'http://player.mais.uol.com.br/player_video_v3.swf?mediaId=15951931',
        'md5': '4f1e26683979715ff64e4e29099cf020',
        'info_dict': {
            'id': '15951931',
            'ext': 'mp4',
            'title': 'Miss simpatia é encontrada morta',
            'description': 'md5:3f8c11a0c0556d66daf7e5b45ef823b2',
            'timestamp': 1470421860,
            'upload_date': '20160805',
        }
    }, {
        'url': 'http://tvuol.uol.com.br/video/incendio-destroi-uma-das-maiores-casas-noturnas-de-londres-04024E9A3268D4C95326',
        'md5': '2850a0e8dfa0a7307e04a96c5bdc5bc2',
        'info_dict': {
            'id': '15954259',
            'ext': 'mp4',
            'title': 'Incêndio destrói uma das maiores casas noturnas de Londres',
            'description': 'Em Londres, um incêndio destruiu uma das maiores boates da cidade. Não há informações sobre vítimas.',
            'timestamp': 1470674520,
            'upload_date': '20160808',
        }
    }, {
        'url': 'http://mais.uol.com.br/static/uolplayer/index.html?mediaId=15951931',
        'only_matching': True,
    }, {
        'url': 'http://mais.uol.com.br/view/15954259',
        'only_matching': True,
    }, {
        'url': 'http://noticias.band.uol.com.br/brasilurgente/video/2016/08/05/15951931/miss-simpatia-e-encontrada-morta.html',
        'only_matching': True,
    }, {
        'url': 'http://videos.band.uol.com.br/programa.asp?e=noticias&pr=brasil-urgente&v=15951931&t=Policia-desmonte-base-do-PCC-na-Cracolandia',
        'only_matching': True,
    }, {
        'url': 'http://mais.uol.com.br/view/cphaa0gl2x8r/incendio-destroi-uma-das-maiores-casas-noturnas-de-londres-04024E9A3268D4C95326',
        'only_matching': True,
    }, {
        'url': 'http://noticias.uol.com.br//videos/assistir.htm?video=rafaela-silva-inspira-criancas-no-judo-04024D983968D4C95326',
        'only_matching': True,
    }, {
        'url': 'http://mais.uol.com.br/view/e0qbgxid79uv/15275470',
        'only_matching': True,
    }]

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

        video_data = self._download_json(
            # https://api.mais.uol.com.br/apiuol/v4/player/data/[MEDIA_ID]
            'https://api.mais.uol.com.br/apiuol/v3/media/detail/' + video_id,
            video_id)['item']
        media_id = compat_str(video_data['mediaId'])
        title = video_data['title']
        ver = video_data.get('revision', 2)

        uol_formats = self._download_json(
            'https://croupier.mais.uol.com.br/v3/formats/%s/jsonp' % media_id,
            media_id)
        quality = qualities(['mobile', 'WEBM', '360p', '720p', '1080p'])
        formats = []
        for format_id, f in uol_formats.items():
            if not isinstance(f, dict):
                continue
            f_url = f.get('url') or f.get('secureUrl')
            if not f_url:
                continue
            query = {
                'ver': ver,
                'r': 'http://mais.uol.com.br',
            }
            for k in ('token', 'sign'):
                v = f.get(k)
                if v:
                    query[k] = v
            f_url = update_url_query(f_url, query)
            if format_id == 'HLS':
                m3u8_formats = self._extract_m3u8_formats(
                    f_url, media_id, 'mp4', 'm3u8_native',
                    m3u8_id='hls', fatal=False)
                encoded_query = compat_urllib_parse_urlencode(query)
                for m3u8_f in m3u8_formats:
                    m3u8_f['extra_param_to_segment_url'] = encoded_query
                    m3u8_f['url'] = update_url_query(m3u8_f['url'], query)
                formats.extend(m3u8_formats)
                continue
            formats.append({
                'format_id': format_id,
                'url': f_url,
                'quality': quality(format_id),
            })
        self._sort_formats(formats)

        tags = []
        for tag in video_data.get('tags', []):
            tag_description = tag.get('description')
            if not tag_description:
                continue
            tags.append(tag_description)

        thumbnails = []
        for q in ('Small', 'Medium', 'Wmedium', 'Large', 'Wlarge', 'Xlarge'):
            q_url = video_data.get('thumb' + q)
            if not q_url:
                continue
            thumbnails.append({
                'id': q,
                'url': q_url,
            })

        return {
            'id': media_id,
            'title': title,
            'description': clean_html(video_data.get('description')),
            'thumbnails': thumbnails,
            'duration': parse_duration(video_data.get('duration')),
            'tags': tags,
            'formats': formats,
            'timestamp': parse_iso8601(video_data.get('publishDate'), ' '),
            'view_count': int_or_none(video_data.get('viewsQtty')),
        }


code:
import re

from .common import InfoExtractor
from ..utils import (
    float_or_none,
    ExtractorError,
)

class UplynkIE(InfoExtractor):
    IE_NAME = 'uplynk'
    _VALID_URL = r'https?://.*?\.uplynk\.com/(?P<path>ext/[0-9a-f]{32}/(?P<external_id>[^/?&]+)|(?P<id>[0-9a-f]{32}))\.(?:m3u8|json)(?:.*?\bpbs=(?P<session_id>[^&]+))?'
    _TEST = {
        'url': 'http://content.uplynk.com/e89eaf2ce9054aa89d92ddb2d817a52e.m3u8',
        'info_dict': {
            'id': 'e89eaf2ce9054aa89d92ddb2d817a52e',
            'ext': 'mp4',
            'title': '030816-kgo-530pm-solar-eclipse-vid_web.mp4',
            'uploader_id': '4413701bf5a1488db55b767f8ae9d4fa',
        },
        'params': {
            # m3u8 download
            'skip_download': True,
        },
    }

    def _extract_uplynk_info(self, uplynk_content_url):
        path, external_id, video_id, session_id = re.match(UplynkIE._VALID_URL, uplynk_content_url).groups()
        display_id = video_id or external_id
        formats, subtitles = self._extract_m3u8_formats_and_subtitles(
            'http://content.uplynk.com/%s.m3u8' % path,
            display_id, 'mp4', 'm3u8_native')
        if session_id:
            for f in formats:
                f['extra_param_to_segment_url'] = 'pbs=' + session_id
        self._sort_formats(formats)
        asset = self._download_json('http://content.uplynk.com/player/assetinfo/%s.json' % path, display_id)
        if asset.get('error') == 1:
            raise ExtractorError('% said: %s' % (self.IE_NAME, asset['msg']), expected=True)

        return {
            'id': asset['asset'],
            'title': asset['desc'],
            'thumbnail': asset.get('default_poster_url'),
            'duration': float_or_none(asset.get('duration')),
            'uploader_id': asset.get('owner'),
            'formats': formats,
            'subtitles': subtitles,
        }

    def _real_extract(self, url):
        return self._extract_uplynk_info(url)

class UplynkPreplayIE(UplynkIE):
    IE_NAME = 'uplynk:preplay'
    _VALID_URL = r'https?://.*?\.uplynk\.com/preplay2?/(?P<path>ext/[0-9a-f]{32}/(?P<external_id>[^/?&]+)|(?P<id>[0-9a-f]{32}))\.json'
    _TEST = None

    def _real_extract(self, url):
        path, external_id, video_id = self._match_valid_url(url).groups()
        display_id = video_id or external_id
        preplay = self._download_json(url, display_id)
        content_url = 'http://content.uplynk.com/%s.m3u8' % path
        session_id = preplay.get('sid')
        if session_id:
            content_url += '?pbs=' + session_id
        return self._extract_uplynk_info(content_url)


code:
import urllib.parse

from .common import InfoExtractor
from ..utils import unified_strdate

class UrortIE(InfoExtractor):
    IE_DESC = 'NRK P3 Urørt'
    _VALID_URL = r'https?://(?:www\.)?urort\.p3\.no/#!/Band/(?P<id>[^/]+)$'

    _TEST = {
        'url': 'https://urort.p3.no/#!/Band/Gerilja',
        'md5': '5ed31a924be8a05e47812678a86e127b',
        'info_dict': {
            'id': '33124-24',
            'ext': 'mp3',
            'title': 'The Bomb',
            'thumbnail': r're:^https?://.+\.jpg',
            'uploader': 'Gerilja',
            'uploader_id': 'Gerilja',
            'upload_date': '20100323',
        },
        'params': {
            'matchtitle': '^The Bomb$',  # To test, we want just one video
        }
    }

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

        fstr = urllib.parse.quote("InternalBandUrl eq '%s'" % playlist_id)
        json_url = 'http://urort.p3.no/breeze/urort/TrackDTOViews?$filter=%s&$orderby=Released%%20desc&$expand=Tags%%2CFiles' % fstr
        songs = self._download_json(json_url, playlist_id)
        entries = []
        for s in songs:
            formats = [{
                'tbr': f.get('Quality'),
                'ext': f['FileType'],
                'format_id': '%s-%s' % (f['FileType'], f.get('Quality', '')),
                'url': 'http://p3urort.blob.core.windows.net/tracks/%s' % f['FileRef'],
                'quality': 3 if f['FileType'] == 'mp3' else 2,
            } for f in s['Files']]
            self._sort_formats(formats)
            e = {
                'id': '%d-%s' % (s['BandId'], s['$id']),
                'title': s['Title'],
                'uploader_id': playlist_id,
                'uploader': s.get('BandName', playlist_id),
                'thumbnail': 'http://urort.p3.no/cloud/images/%s' % s['Image'],
                'upload_date': unified_strdate(s.get('Released')),
                'formats': formats,
            }
            entries.append(e)

        return {
            '_type': 'playlist',
            'id': playlist_id,
            'title': playlist_id,
            'entries': entries,
        }


code:
from .common import InfoExtractor
from ..utils import (
    dict_get,
    ExtractorError,
    int_or_none,
    ISO639Utils,
    parse_age_limit,
    try_get,
    unified_timestamp,
)

class URPlayIE(InfoExtractor):
    _VALID_URL = r'https?://(?:www\.)?ur(?:play|skola)\.se/(?:program|Produkter)/(?P<id>[0-9]+)'
    _TESTS = [{
        'url': 'https://urplay.se/program/203704-ur-samtiden-livet-universum-och-rymdens-markliga-musik-om-vetenskap-kritiskt-tankande-och-motstand',
        'md5': 'ff5b0c89928f8083c74bbd5099c9292d',
        'info_dict': {
            'id': '203704',
            'ext': 'mp4',
            'title': 'UR Samtiden - Livet, universum och rymdens märkliga musik : Om vetenskap, kritiskt tänkande och motstånd',
            'description': 'md5:5344508a52aa78c1ced6c1b8b9e44e9a',
            'timestamp': 1513292400,
            'upload_date': '20171214',
            'series': 'UR Samtiden - Livet, universum och rymdens märkliga musik',
            'duration': 2269,
            'categories': ['Vetenskap & teknik'],
            'tags': ['Kritiskt tänkande', 'Vetenskap', 'Vetenskaplig verksamhet'],
            'episode': 'Om vetenskap, kritiskt tänkande och motstånd',
            'age_limit': 15,
        },
    }, {
        'url': 'https://urskola.se/Produkter/190031-Tripp-Trapp-Trad-Sovkudde',
        'info_dict': {
            'id': '190031',
            'ext': 'mp4',
            'title': 'Tripp, Trapp, Träd : Sovkudde',
            'description': 'md5:b86bffdae04a7e9379d1d7e5947df1d1',
            'timestamp': 1440086400,
            'upload_date': '20150820',
            'series': 'Tripp, Trapp, Träd',
            'duration': 865,
            'tags': ['Sova'],
            'episode': 'Sovkudde',
        },
    }, {
        'url': 'http://urskola.se/Produkter/155794-Smasagor-meankieli-Grodan-i-vida-varlden',
        'only_matching': True,
    }]

    def _real_extract(self, url):
        video_id = self._match_id(url)
        url = url.replace('skola.se/Produkter', 'play.se/program')
        webpage = self._download_webpage(url, video_id)
        urplayer_data = self._search_nextjs_data(webpage, video_id, fatal=False) or {}
        if urplayer_data:
            urplayer_data = try_get(urplayer_data, lambda x: x['props']['pageProps']['program'], dict)
            if not urplayer_data:
                raise ExtractorError('Unable to parse __NEXT_DATA__')
        else:
            accessible_episodes = self._parse_json(self._html_search_regex(
                r'data-react-class="routes/Product/components/ProgramContainer/ProgramContainer"[^>]+data-react-props="({.+?})"',
                webpage, 'urplayer data'), video_id)['accessibleEpisodes']
            urplayer_data = next(e for e in accessible_episodes if e.get('id') == int_or_none(video_id))
        episode = urplayer_data['title']

        host = self._download_json('http://streaming-loadbalancer.ur.se/loadbalancer.json', video_id)['redirect']
        formats = []
        urplayer_streams = urplayer_data.get('streamingInfo', {})

        for k, v in urplayer_streams.get('raw', {}).items():
            if not (k in ('sd', 'hd') and isinstance(v, dict)):
                continue
            file_http = v.get('location')
            if file_http:
                formats.extend(self._extract_wowza_formats(
                    'http://%s/%splaylist.m3u8' % (host, file_http),
                    video_id, skip_protocols=['f4m', 'rtmp', 'rtsp']))
        self._sort_formats(formats)

        subtitles = {}

        def parse_lang_code(code):
            "3-character language code or None (utils candidate)"
            if code is None:
                return
            lang = code.lower()
            if not ISO639Utils.long2short(lang):
                lang = ISO639Utils.short2long(lang)
            return lang or None

        for k, v in (urplayer_data['streamingInfo'].get('sweComplete') or {}).items():
            if (k in ('sd', 'hd') or not isinstance(v, dict)):
                continue
            lang, sttl_url = (v.get(kk) for kk in ('language', 'location', ))
            if not sttl_url:
                continue
            lang = parse_lang_code(lang)
            if not lang:
                continue
            sttl = subtitles.get(lang) or []
            sttl.append({'ext': k, 'url': sttl_url, })
            subtitles[lang] = sttl

        image = urplayer_data.get('image') or {}
        thumbnails = []
        for k, v in image.items():
            t = {
                'id': k,
                'url': v,
            }
            wh = k.split('x')
            if len(wh) == 2:
                t.update({
                    'width': int_or_none(wh[0]),
                    'height': int_or_none(wh[1]),
                })
            thumbnails.append(t)

        series = urplayer_data.get('series') or {}
        series_title = dict_get(series, ('seriesTitle', 'title')) or dict_get(urplayer_data, ('seriesTitle', 'mainTitle'))

        return {
            'id': video_id,
            'title': '%s : %s' % (series_title, episode) if series_title else episode,
            'description': urplayer_data.get('description'),
            'thumbnails': thumbnails,
            'timestamp': unified_timestamp(urplayer_data.get('publishedAt')),
            'series': series_title,
            'formats': formats,
            'duration': int_or_none(urplayer_data.get('duration')),
            'categories': urplayer_data.get('categories'),
            'tags': urplayer_data.get('keywords'),
            'season': series.get('label'),
            'episode': episode,
            'episode_number': int_or_none(urplayer_data.get('episodeNumber')),
            'age_limit': parse_age_limit(min(try_get(a, lambda x: x['from'], int) or 0
                                             for a in urplayer_data.get('ageRanges', []))),
            'subtitles': subtitles,
        }


code:
from .nbc import NBCIE

class USANetworkIE(NBCIE):
    _VALID_URL = r'https?(?P<permalink>://(?:www\.)?usanetwork\.com/(?:[^/]+/videos?|movies?)/(?:[^/]+/)?(?P<id>\d+))'
    _TESTS = [{
        'url': 'https://www.usanetwork.com/peacock-trailers/video/intelligence-trailer/4185302',
        'info_dict': {
            'id': '4185302',
            'ext': 'mp4',
            'title': 'Intelligence (Trailer)',
            'description': 'A maverick NSA agent enlists the help of a junior systems analyst in a workplace power grab.',
            'upload_date': '20200715',
            'timestamp': 1594785600,
            'uploader': 'NBCU-MPAT',
        },
        'params': {
            # m3u8 download
            'skip_download': True,
        },
    }]


code:
from .common import InfoExtractor
from ..utils import (
    ExtractorError,
    get_element_by_attribute,
    parse_duration,
    try_get,
    update_url_query,
)
from ..compat import compat_str

class USATodayIE(InfoExtractor):
    _VALID_URL = r'https?://(?:www\.)?usatoday\.com/(?:[^/]+/)*(?P<id>[^?/#]+)'
    _TESTS = [{
        # Brightcove Partner ID = 29906170001
        'url': 'http://www.usatoday.com/media/cinematic/video/81729424/us-france-warn-syrian-regime-ahead-of-new-peace-talks/',
        'md5': '033587d2529dc3411a1ab3644c3b8827',
        'info_dict': {
            'id': '4799374959001',
            'ext': 'mp4',
            'title': 'US, France warn Syrian regime ahead of new peace talks',
            'timestamp': 1457891045,
            'description': 'md5:7e50464fdf2126b0f533748d3c78d58f',
            'uploader_id': '29906170001',
            'upload_date': '20160313',
        }
    }, {
        # ui-video-data[asset_metadata][items][brightcoveaccount] = 28911775001
        'url': 'https://www.usatoday.com/story/tech/science/2018/08/21/yellowstone-supervolcano-eruption-stop-worrying-its-blow/973633002/',
        'info_dict': {
            'id': '5824495846001',
            'ext': 'mp4',
            'title': 'Yellowstone more likely to crack rather than explode',
            'timestamp': 1534790612,
            'description': 'md5:3715e7927639a4f16b474e9391687c62',
            'uploader_id': '28911775001',
            'upload_date': '20180820',
        }
    }]
    BRIGHTCOVE_URL_TEMPLATE = 'http://players.brightcove.net/%s/default_default/index.html?videoId=%s'

    def _real_extract(self, url):
        display_id = self._match_id(url)
        webpage = self._download_webpage(update_url_query(url, {'ajax': 'true'}), display_id)
        ui_video_data = get_element_by_attribute('class', 'ui-video-data', webpage)
        if not ui_video_data:
            raise ExtractorError('no video on the webpage', expected=True)
        video_data = self._parse_json(ui_video_data, display_id)
        item = try_get(video_data, lambda x: x['asset_metadata']['items'], dict) or {}

        return {
            '_type': 'url_transparent',
            'url': self.BRIGHTCOVE_URL_TEMPLATE % (item.get('brightcoveaccount', '29906170001'), item.get('brightcoveid') or video_data['brightcove_id']),
            'id': compat_str(video_data['id']),
            'title': video_data['title'],
            'thumbnail': video_data.get('thumbnail'),
            'description': video_data.get('description'),
            'duration': parse_duration(video_data.get('length')),
            'ie_key': 'BrightcoveNew',
        }


code:
import random
import re

from .common import InfoExtractor
from ..compat import (
    compat_str,
    compat_urlparse,
)
from ..utils import (
    encode_data_uri,
    ExtractorError,
    int_or_none,
    float_or_none,
    join_nonempty,
    mimetype2ext,
    str_or_none,
)

class UstreamIE(InfoExtractor):
    _VALID_URL = r'https?://(?:www\.)?(?:ustream\.tv|video\.ibm\.com)/(?P<type>recorded|embed|embed/recorded)/(?P<id>\d+)'
    IE_NAME = 'ustream'
    _EMBED_REGEX = [r'<iframe[^>]+?src=(["\'])(?P<url>https?://(?:www\.)?(?:ustream\.tv|video\.ibm\.com)/embed/.+?)\1']
    _TESTS = [{
        'url': 'http://www.ustream.tv/recorded/20274954',
        'md5': '088f151799e8f572f84eb62f17d73e5c',
        'info_dict': {
            'id': '20274954',
            'ext': 'flv',
            'title': 'Young Americans for Liberty February 7, 2012 2:28 AM',
            'description': 'Young Americans for Liberty February 7, 2012 2:28 AM',
            'timestamp': 1328577035,
            'upload_date': '20120207',
            'uploader': 'yaliberty',
            'uploader_id': '6780869',
        },
    }, {
        # From http://sportscanada.tv/canadagames/index.php/week2/figure-skating/444
        # Title and uploader available only from params JSON
        'url': 'http://www.ustream.tv/embed/recorded/59307601?ub=ff0000&lc=ff0000&oc=ffffff&uc=ffffff&v=3&wmode=direct',
        'md5': '5a2abf40babeac9812ed20ae12d34e10',
        'info_dict': {
            'id': '59307601',
            'ext': 'flv',
            'title': '-CG11- Canada Games Figure Skating',
            'uploader': 'sportscanadatv',
        },
        'skip': 'This Pro Broadcaster has chosen to remove this video from the ustream.tv site.',
    }, {
        'url': 'http://www.ustream.tv/embed/10299409',
        'info_dict': {
            'id': '10299409',
        },
        'playlist_count': 3,
    }, {
        'url': 'http://www.ustream.tv/recorded/91343263',
        'info_dict': {
            'id': '91343263',
            'ext': 'mp4',
            'title': 'GitHub Universe - General Session - Day 1',
            'upload_date': '20160914',
            'description': 'GitHub Universe - General Session - Day 1',
            'timestamp': 1473872730,
            'uploader': 'wa0dnskeqkr',
            'uploader_id': '38977840',
        },
        'params': {
            'skip_download': True,  # m3u8 download
        },
    }, {
        'url': 'https://video.ibm.com/embed/recorded/128240221?&autoplay=true&controls=true&volume=100',
        'only_matching': True,
    }]

    def _get_stream_info(self, url, video_id, app_id_ver, extra_note=None):
        def num_to_hex(n):
            return hex(n)[2:]

        rnd = random.randrange

        if not extra_note:
            extra_note = ''

        conn_info = self._download_json(
            'http://r%d-1-%s-recorded-lp-live.ums.ustream.tv/1/ustream' % (rnd(1e8), video_id),
            video_id, note='Downloading connection info' + extra_note,
            query={
                'type': 'viewer',
                'appId': app_id_ver[0],
                'appVersion': app_id_ver[1],
                'rsid': '%s:%s' % (num_to_hex(rnd(1e8)), num_to_hex(rnd(1e8))),
                'rpin': '_rpin.%d' % rnd(1e15),
                'referrer': url,
                'media': video_id,
                'application': 'recorded',
            })
        host = conn_info[0]['args'][0]['host']
        connection_id = conn_info[0]['args'][0]['connectionId']

        return self._download_json(
            'http://%s/1/ustream?connectionId=%s' % (host, connection_id),
            video_id, note='Downloading stream info' + extra_note)

    def _get_streams(self, url, video_id, app_id_ver):
        # Sometimes the return dict does not have 'stream'
        for trial_count in range(3):
            stream_info = self._get_stream_info(
                url, video_id, app_id_ver,
                extra_note=' (try %d)' % (trial_count + 1) if trial_count > 0 else '')
            if 'stream' in stream_info[0]['args'][0]:
                return stream_info[0]['args'][0]['stream']
        return []

    def _parse_segmented_mp4(self, dash_stream_info):
        def resolve_dash_template(template, idx, chunk_hash):
            return template.replace('%', compat_str(idx), 1).replace('%', chunk_hash)

        formats = []
        for stream in dash_stream_info['streams']:
            # Use only one provider to avoid too many formats
            provider = dash_stream_info['providers'][0]
            fragments = [{
                'url': resolve_dash_template(
                    provider['url'] + stream['initUrl'], 0, dash_stream_info['hashes']['0'])
            }]
            for idx in range(dash_stream_info['videoLength'] // dash_stream_info['chunkTime']):
                fragments.append({
                    'url': resolve_dash_template(
                        provider['url'] + stream['segmentUrl'], idx,
                        dash_stream_info['hashes'][compat_str(idx // 10 * 10)])
                })
            content_type = stream['contentType']
            kind = content_type.split('/')[0]
            f = {
                'format_id': join_nonempty(
                    'dash', kind, str_or_none(stream.get('bitrate'))),
                'protocol': 'http_dash_segments',
                # TODO: generate a MPD doc for external players?
                'url': encode_data_uri(b'<MPD/>', 'text/xml'),
                'ext': mimetype2ext(content_type),
                'height': stream.get('height'),
                'width': stream.get('width'),
                'fragments': fragments,
            }
            if kind == 'video':
                f.update({
                    'vcodec': stream.get('codec'),
                    'acodec': 'none',
                    'vbr': stream.get('bitrate'),
                })
            else:
                f.update({
                    'vcodec': 'none',
                    'acodec': stream.get('codec'),
                    'abr': stream.get('bitrate'),
                })
            formats.append(f)
        return formats

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

        # some sites use this embed format (see: https://github.com/ytdl-org/youtube-dl/issues/2990)
        if m.group('type') == 'embed/recorded':
            video_id = m.group('id')
            desktop_url = 'http://www.ustream.tv/recorded/' + video_id
            return self.url_result(desktop_url, 'Ustream')
        if m.group('type') == 'embed':
            video_id = m.group('id')
            webpage = self._download_webpage(url, video_id)
            content_video_ids = self._parse_json(self._search_regex(
                r'ustream\.vars\.offAirContentVideoIds=([^;]+);', webpage,
                'content video IDs'), video_id)
            return self.playlist_result(
                map(lambda u: self.url_result('http://www.ustream.tv/recorded/' + u, 'Ustream'), content_video_ids),
                video_id)

        params = self._download_json(
            'https://api.ustream.tv/videos/%s.json' % video_id, video_id)

        error = params.get('error')
        if error:
            raise ExtractorError(
                '%s returned error: %s' % (self.IE_NAME, error), expected=True)

        video = params['video']

        title = video['title']
        filesize = float_or_none(video.get('file_size'))

        formats = [{
            'id': video_id,
            'url': video_url,
            'ext': format_id,
            'filesize': filesize,
        } for format_id, video_url in video['media_urls'].items() if video_url]

        if not formats:
            hls_streams = self._get_streams(url, video_id, app_id_ver=(11, 2))
            if hls_streams:
                # m3u8_native leads to intermittent ContentTooShortError
                formats.extend(self._extract_m3u8_formats(
                    hls_streams[0]['url'], video_id, ext='mp4', m3u8_id='hls'))

            '''
            # DASH streams handling is incomplete as 'url' is missing
            dash_streams = self._get_streams(url, video_id, app_id_ver=(3, 1))
            if dash_streams:
                formats.extend(self._parse_segmented_mp4(dash_streams))
            '''

        self._sort_formats(formats)

        description = video.get('description')
        timestamp = int_or_none(video.get('created_at'))
        duration = float_or_none(video.get('length'))
        view_count = int_or_none(video.get('views'))

        uploader = video.get('owner', {}).get('username')
        uploader_id = video.get('owner', {}).get('id')

        thumbnails = [{
            'id': thumbnail_id,
            'url': thumbnail_url,
        } for thumbnail_id, thumbnail_url in video.get('thumbnail', {}).items()]

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

class UstreamChannelIE(InfoExtractor):
    _VALID_URL = r'https?://(?:www\.)?ustream\.tv/channel/(?P<slug>.+)'
    IE_NAME = 'ustream:channel'
    _TEST = {
        'url': 'http://www.ustream.tv/channel/channeljapan',
        'info_dict': {
            'id': '10874166',
        },
        'playlist_mincount': 17,
    }

    def _real_extract(self, url):
        m = self._match_valid_url(url)
        display_id = m.group('slug')
        webpage = self._download_webpage(url, display_id)
        channel_id = self._html_search_meta('ustream:channel_id', webpage)

        BASE = 'http://www.ustream.tv'
        next_url = '/ajax/socialstream/videos/%s/1.json' % channel_id
        video_ids = []
        while next_url:
            reply = self._download_json(
                compat_urlparse.urljoin(BASE, next_url), display_id,
                note='Downloading video information (next: %d)' % (len(video_ids) + 1))
            video_ids.extend(re.findall(r'data-content-id="(\d.*)"', reply['data']))
            next_url = reply['nextUrl']

        entries = [
            self.url_result('http://www.ustream.tv/recorded/' + vid, 'Ustream')
            for vid in video_ids]
        return {
            '_type': 'playlist',
            'id': channel_id,
            'display_id': display_id,
            'entries': entries,
        }


code:
from .common import InfoExtractor
from ..utils import (
    int_or_none,
    unified_strdate,
    unescapeHTML,
)

class UstudioIE(InfoExtractor):
    IE_NAME = 'ustudio'
    _VALID_URL = r'https?://(?:(?:www|v1)\.)?ustudio\.com/video/(?P<id>[^/]+)/(?P<display_id>[^/?#&]+)'
    _TEST = {
        'url': 'http://ustudio.com/video/Uxu2my9bgSph/san_francisco_golden_gate_bridge',
        'md5': '58bbfca62125378742df01fc2abbdef6',
        'info_dict': {
            'id': 'Uxu2my9bgSph',
            'display_id': 'san_francisco_golden_gate_bridge',
            'ext': 'mp4',
            'title': 'San Francisco: Golden Gate Bridge',
            'description': 'md5:23925500697f2c6d4830e387ba51a9be',
            'thumbnail': r're:^https?://.*\.jpg$',
            'upload_date': '20111107',
            'uploader': 'Tony Farley',
        }
    }

    def _real_extract(self, url):
        video_id, display_id = self._match_valid_url(url).groups()

        config = self._download_xml(
            'http://v1.ustudio.com/embed/%s/ustudio/config.xml' % video_id,
            display_id)

        def extract(kind):
            return [{
                'url': unescapeHTML(item.attrib['url']),
                'width': int_or_none(item.get('width')),
                'height': int_or_none(item.get('height')),
            } for item in config.findall('./qualities/quality/%s' % kind) if item.get('url')]

        formats = extract('video')
        self._sort_formats(formats)

        webpage = self._download_webpage(url, display_id)

        title = self._og_search_title(webpage)
        upload_date = unified_strdate(self._search_regex(
            r'(?s)Uploaded by\s*.+?\s*on\s*<span>([^<]+)</span>',
            webpage, 'upload date', fatal=False))
        uploader = self._search_regex(
            r'Uploaded by\s*<a[^>]*>([^<]+)<',
            webpage, 'uploader', fatal=False)

        return {
            'id': video_id,
            'display_id': display_id,
            'title': title,
            'description': self._og_search_description(webpage),
            'thumbnails': extract('image'),
            'upload_date': upload_date,
            'uploader': uploader,
            'formats': formats,
        }

class UstudioEmbedIE(InfoExtractor):
    IE_NAME = 'ustudio:embed'
    _VALID_URL = r'https?://(?:(?:app|embed)\.)?ustudio\.com/embed/(?P<uid>[^/]+)/(?P<id>[^/]+)'
    _TEST = {
        'url': 'http://app.ustudio.com/embed/DeN7VdYRDKhP/Uw7G1kMCe65T',
        'md5': '47c0be52a09b23a7f40de9469cec58f4',
        'info_dict': {
            'id': 'Uw7G1kMCe65T',
            'ext': 'mp4',
            'title': '5 Things IT Should Know About Video',
            'description': 'md5:93d32650884b500115e158c5677d25ad',
            'uploader_id': 'DeN7VdYRDKhP',
        }
    }

    def _real_extract(self, url):
        uploader_id, video_id = self._match_valid_url(url).groups()
        video_data = self._download_json(
            'http://app.ustudio.com/embed/%s/%s/config.json' % (uploader_id, video_id),
            video_id)['videos'][0]
        title = video_data['name']

        formats = []
        for ext, qualities in video_data.get('transcodes', {}).items():
            for quality in qualities:
                quality_url = quality.get('url')
                if not quality_url:
                    continue
                height = int_or_none(quality.get('height'))
                formats.append({
                    'format_id': '%s-%dp' % (ext, height) if height else ext,
                    'url': quality_url,
                    'width': int_or_none(quality.get('width')),
                    'height': height,
                })
        self._sort_formats(formats)

        thumbnails = []
        for image in video_data.get('images', []):
            image_url = image.get('url')
            if not image_url:
                continue
            thumbnails.append({
                'url': image_url,
            })

        return {
            'id': video_id,
            'title': title,
            'description': video_data.get('description'),
            'duration': int_or_none(video_data.get('duration')),
            'uploader_id': uploader_id,
            'tags': video_data.get('keywords'),
            'thumbnails': thumbnails,
            'formats': formats,
        }


code:
from .common import InfoExtractor
from ..utils import (
    dict_get,
    int_or_none,
    str_or_none,
    try_get,
    unified_strdate,
    url_or_none,
)

class UtreonIE(InfoExtractor):
    _VALID_URL = r'https?://(?:www\.)?utreon.com/v/(?P<id>[a-zA-Z0-9_-]+)'
    _TESTS = [{
        'url': 'https://utreon.com/v/z_I7ikQbuDw',
        'info_dict': {
            'id': 'z_I7ikQbuDw',
            'ext': 'mp4',
            'title': 'Freedom Friday meditation - Rising in the wind',
            'description': 'md5:a9bf15a42434a062fe313b938343ad1b',
            'uploader': 'Heather Dawn Elemental Health',
            'thumbnail': 'https://data-1.utreon.com/v/MG/M2/NT/z_I7ikQbuDw/z_I7ikQbuDw_preview.jpg',
            'release_date': '20210723',
        }
    }, {
        'url': 'https://utreon.com/v/jerJw5EOOVU',
        'info_dict': {
            'id': 'jerJw5EOOVU',
            'ext': 'mp4',
            'title': 'When I\'m alone, I love to reflect in peace, to make my dreams come true... [Quotes and Poems]',
            'description': 'md5:61ee6c2da98be51b04b969ca80273aaa',
            'uploader': 'Frases e Poemas Quotes and Poems',
            'thumbnail': 'https://data-1.utreon.com/v/Mz/Zh/ND/jerJw5EOOVU/jerJw5EOOVU_89af85470a4b16eededde7f8674c96d9_cover.jpg',
            'release_date': '20210723',
        }
    }, {
        'url': 'https://utreon.com/v/C4ZxXhYBBmE',
        'info_dict': {
            'id': 'C4ZxXhYBBmE',
            'ext': 'mp4',
            'title': 'Biden’s Capital Gains Tax Rate to Test World’s Highest',
            'description': 'md5:fb5a6c2e506f013cc76f133f673bc5c8',
            'uploader': 'Nomad Capitalist',
            'thumbnail': 'https://data-1.utreon.com/v/ZD/k1/Mj/C4ZxXhYBBmE/C4ZxXhYBBmE_628342076198c9c06dd6b2c665978584_cover.jpg',
            'release_date': '20210723',
        }
    }, {
        'url': 'https://utreon.com/v/Y-stEH-FBm8',
        'info_dict': {
            'id': 'Y-stEH-FBm8',
            'ext': 'mp4',
            'title': 'Creeper-Chan Pranks Steve! 💚 [MINECRAFT ANIME]',
            'description': 'md5:7a48450b0d761b96dec194be0c5ecb5f',
            'uploader': 'Merryweather Comics',
            'thumbnail': 'https://data-1.utreon.com/v/MT/E4/Zj/Y-stEH-FBm8/Y-stEH-FBm8_5290676a41a4a1096db133b09f54f77b_cover.jpg',
            'release_date': '20210718',
        }},
    ]

    def _real_extract(self, url):
        video_id = self._match_id(url)
        json_data = self._download_json(
            'https://api.utreon.com/v1/videos/' + video_id,
            video_id)
        videos_json = json_data['videos']
        formats = [{
            'url': format_url,
            'format_id': format_key.split('_')[1],
            'height': int(format_key.split('_')[1][:-1]),
        } for format_key, format_url in videos_json.items() if url_or_none(format_url)]
        self._sort_formats(formats)
        thumbnail = url_or_none(dict_get(json_data, ('cover_image_url', 'preview_image_url')))
        return {
            'id': video_id,
            'title': json_data['title'],
            'formats': formats,
            'description': str_or_none(json_data.get('description')),
            'duration': int_or_none(json_data.get('duration')),
            'uploader': str_or_none(try_get(json_data, lambda x: x['channel']['title'])),
            'thumbnail': thumbnail,
            'release_date': unified_strdate(json_data.get('published_datetime')),
        }


code:
from .common import InfoExtractor
from ..utils import (
    clean_html,
    parse_qs,
    remove_start,
)

class Varzesh3IE(InfoExtractor):
    _VALID_URL = r'https?://(?:www\.)?video\.varzesh3\.com/(?:[^/]+/)+(?P<id>[^/]+)/?'
    _TESTS = [{
        'url': 'http://video.varzesh3.com/germany/bundesliga/5-%D9%88%D8%A7%DA%A9%D9%86%D8%B4-%D8%A8%D8%B1%D8%AA%D8%B1-%D8%AF%D8%B1%D9%88%D8%A7%D8%B2%D9%87%E2%80%8C%D8%A8%D8%A7%D9%86%D8%A7%D9%86%D8%9B%D9%87%D9%81%D8%AA%D9%87-26-%D8%A8%D9%88%D9%86%D8%AF%D8%B3/',
        'md5': '2a933874cb7dce4366075281eb49e855',
        'info_dict': {
            'id': '76337',
            'ext': 'mp4',
            'title': '۵ واکنش برتر دروازه‌بانان؛هفته ۲۶ بوندسلیگا',
            'description': 'فصل ۲۰۱۵-۲۰۱۴',
            'thumbnail': r're:^https?://.*\.jpg$',
        },
        'skip': 'HTTP 404 Error',
    }, {
        'url': 'http://video.varzesh3.com/video/112785/%D8%AF%D9%84%D9%87-%D8%B9%D9%84%DB%8C%D8%9B-%D8%B3%D8%AA%D8%A7%D8%B1%D9%87-%D9%86%D9%88%D8%B8%D9%87%D9%88%D8%B1-%D9%84%DB%8C%DA%AF-%D8%A8%D8%B1%D8%AA%D8%B1-%D8%AC%D8%B2%DB%8C%D8%B1%D9%87',
        'md5': '841b7cd3afbc76e61708d94e53a4a4e7',
        'info_dict': {
            'id': '112785',
            'ext': 'mp4',
            'title': 'دله علی؛ ستاره نوظهور لیگ برتر جزیره',
            'description': 'فوتبال 120',
        },
        'expected_warnings': ['description'],
    }]

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

        webpage = self._download_webpage(url, display_id)

        video_url = self._search_regex(
            r'<source[^>]+src="([^"]+)"', webpage, 'video url')

        title = remove_start(self._html_extract_title(webpage), 'ویدیو ورزش 3 | ')

        description = self._html_search_regex(
            r'(?s)
(.+?)
',
            webpage, 'description', default=None)
        if description is None:
            description = clean_html(self._html_search_meta('description', webpage))

        thumbnail = self._og_search_thumbnail(webpage, default=None)
        if thumbnail is None:
            fb_sharer_url = self._search_regex(
                r']+href="(https?://www\.facebook\.com/sharer/sharer\.php?[^"]+)"',
                webpage, 'facebook sharer URL', fatal=False)
            sharer_params = parse_qs(fb_sharer_url)
            thumbnail = sharer_params.get('p[images][0]', [None])[0]

        video_id = self._search_regex(
            r"<link[^>]+rel='(?:canonical|shortlink)'[^>]+href='/\?p=([^']+)'",
            webpage, display_id, default=None)
        if video_id is None:
            video_id = self._search_regex(
                r'var\s+VideoId\s*=\s*(\d+);', webpage, 'video id',
                default=display_id)

        return {
            'url': video_url,
            'id': video_id,
            'title': title,
            'description': description,
            'thumbnail': thumbnail,
        }


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

class Vbox7IE(InfoExtractor):
    _VALID_URL = r'''(?x)
                    https?://
                        (?:[^/]+\.)?vbox7\.com/
                        (?:
                            play:|
                            (?:
                                emb/external\.php|
                                player/ext\.swf
                            )\?.*?\bvid=
                        )
                        (?P<id>[\da-fA-F]+)
                    '''
    _EMBED_REGEX = [r'<iframe[^>]+src=(?P<q>["\'])(?P<url>(?:https?:)?//vbox7\.com/emb/external\.php.+?)(?P=q)']
    _GEO_COUNTRIES = ['BG']
    _TESTS = [{
        'url': 'http://vbox7.com/play:0946fff23c',
        'md5': 'a60f9ab3a3a2f013ef9a967d5f7be5bf',
        'info_dict': {
            'id': '0946fff23c',
            'ext': 'mp4',
            'title': 'Борисов: Притеснен съм за бъдещето на България',
            'description': 'По думите му е опасно страната ни да бъде обявена за "сигурна"',
            'thumbnail': r're:^https?://.*\.jpg$',
            'timestamp': 1470982814,
            'upload_date': '20160812',
            'uploader': 'zdraveibulgaria',
        },
        'params': {
            'proxy': '127.0.0.1:8118',
        },
    }, {
        'url': 'http://vbox7.com/play:249bb972c2',
        'md5': '99f65c0c9ef9b682b97313e052734c3f',
        'info_dict': {
            'id': '249bb972c2',
            'ext': 'mp4',
            'title': 'Смях! Чудо - чист за секунди - Скрита камера',
        },
        'skip': 'georestricted',
    }, {
        'url': 'http://vbox7.com/emb/external.php?vid=a240d20f9c&autoplay=1',
        'only_matching': True,
    }, {
        'url': 'http://i49.vbox7.com/player/ext.swf?vid=0946fff23c&autoplay=1',
        'only_matching': True,
    }]

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

        response = self._download_json(
            'https://www.vbox7.com/ajax/video/nextvideo.php?vid=%s' % video_id,
            video_id)

        if 'error' in response:
            raise ExtractorError(
                '%s said: %s' % (self.IE_NAME, response['error']), expected=True)

        video = response['options']

        title = video['title']
        video_url = video['src']

        if '/na.mp4' in video_url:
            self.raise_geo_restricted(countries=self._GEO_COUNTRIES)

        uploader = video.get('uploader')

        webpage = self._download_webpage(
            'http://vbox7.com/play:%s' % video_id, video_id, fatal=None)

        info = {}

        if webpage:
            info = self._search_json_ld(
                webpage.replace('"/*@context"', '"@context"'), video_id,
                fatal=False)

        info.update({
            'id': video_id,
            'title': title,
            'url': video_url,
            'uploader': uploader,
            'thumbnail': self._proto_relative_url(
                info.get('thumbnail') or self._og_search_thumbnail(webpage),
                'http:'),
        })
        return info


code:
import re
import json

from .common import InfoExtractor
from ..compat import (
    compat_urllib_parse_unquote,
    compat_urlparse,
)
from ..utils import (
    ExtractorError,
    clean_html,
    get_element_by_id,
)

class VeeHDIE(InfoExtractor):
    _VALID_URL = r'https?://veehd\.com/video/(?P<id>\d+)'

    # Seems VeeHD videos have multiple copies on several servers, all of
    # whom have different MD5 checksums, so omit md5 field in all tests
    _TESTS = [{
        'url': 'http://veehd.com/video/4639434_Solar-Sinter',
        'info_dict': {
            'id': '4639434',
            'ext': 'mp4',
            'title': 'Solar Sinter',
            'uploader_id': 'VideoEyes',
            'description': 'md5:46a840e8692ddbaffb5f81d9885cb457',
        },
        'skip': 'Video deleted',
    }, {
        'url': 'http://veehd.com/video/4905758_Elysian-Fields-Channeling',
        'info_dict': {
            'id': '4905758',
            'ext': 'mp4',
            'title': 'Elysian Fields - Channeling',
            'description': 'md5:360e4e95fdab58aefbea0f2a19e5604b',
            'uploader_id': 'spotted',
        }
    }, {
        'url': 'http://veehd.com/video/2046729_2012-2009-DivX-Trailer',
        'info_dict': {
            'id': '2046729',
            'ext': 'avi',
            'title': '2012 (2009) DivX Trailer',
            'description': 'md5:75435ee95255e6a9838ac6f6f3a2396b',
            'uploader_id': 'Movie_Trailers',
        }
    }]

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

        # VeeHD seems to send garbage on the first request.
        # See https://github.com/ytdl-org/youtube-dl/issues/2102
        self._download_webpage(url, video_id, 'Requesting webpage')
        webpage = self._download_webpage(url, video_id)

        if 'This video has been removed<' in webpage:
            raise ExtractorError('Video %s has been removed' % video_id, expected=True)

        player_path = self._search_regex(
            r'\$\("#playeriframe"\).attr\({src : "(.+?)"',
            webpage, 'player path')
        player_url = compat_urlparse.urljoin(url, player_path)

        self._download_webpage(player_url, video_id, 'Requesting player page')
        player_page = self._download_webpage(
            player_url, video_id, 'Downloading player page')

        video_url = None

        config_json = self._search_regex(
            r'value=\'config=({.+?})\'', player_page, 'config json', default=None)

        if config_json:
            config = json.loads(config_json)
            video_url = compat_urllib_parse_unquote(config['clip']['url'])

        if not video_url:
            video_url = self._html_search_regex(
                r'<embed[^>]+type="video/divx"[^>]+src="([^"]+)"',
                player_page, 'video url', default=None)

        if not video_url:
            iframe_src = self._search_regex(
                r']+src="/?([^"]+)"', player_page, 'iframe url')
            iframe_url = 'http://veehd.com/%s' % iframe_src

            self._download_webpage(iframe_url, video_id, 'Requesting iframe page')
            iframe_page = self._download_webpage(
                iframe_url, video_id, 'Downloading iframe page')

            video_url = self._search_regex(
                r"file\s*:\s*'([^']+)'", iframe_page, 'video url')

        title = clean_html(get_element_by_id('videoName', webpage).rpartition('|')[0])
        uploader_id = self._html_search_regex(
            r'(.+?)',
            webpage, 'uploader')
        thumbnail = self._search_regex(
            r'<img id="veehdpreview" src="(.+?)"',
            webpage, 'thumbnail')
        description = self._html_search_regex(
            r'<td class="infodropdown".*?
(.*?)<ul',
            webpage, 'description', flags=re.DOTALL)

        return {
            '_type': 'video',
            'id': video_id,
            'title': title,
            'url': video_url,
            'uploader_id': uploader_id,
            'thumbnail': thumbnail,
            'description': description,
        }


code:
from .common import InfoExtractor

from ..utils import (
    int_or_none,
    mimetype2ext,
    str_or_none,
    unified_timestamp,
    url_or_none,
)

class VeoIE(InfoExtractor):
    _VALID_URL = r'https?://app\.veo\.co/matches/(?P<id>[0-9A-Za-z-_]+)'

    _TESTS = [{
        'url': 'https://app.veo.co/matches/20201027-last-period/',
        'info_dict': {
            'id': '20201027-last-period',
            'ext': 'mp4',
            'title': 'Akidemy u11s v Bradford Boys u11s (Game 3)',
            'thumbnail': 're:https://c.veocdn.com/.+/thumbnail.jpg',
            'upload_date': '20201028',
            'timestamp': 1603847208,
            'duration': 1916,
            'view_count': int,
        }
    }, {
        'url': 'https://app.veo.co/matches/20220313-2022-03-13_u15m-plsjq-vs-csl/',
        'only_matching': True,
    }]

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

        metadata = self._download_json(
            'https://app.veo.co/api/app/matches/%s' % video_id, video_id)

        video_data = self._download_json(
            'https://app.veo.co/api/app/matches/%s/videos' % video_id, video_id, 'Downloading video data')

        formats = []
        for fmt in video_data:
            mimetype = str_or_none(fmt.get('mime_type'))
            format_url = url_or_none(fmt.get('url'))
            # skip configuration file for panoramic video
            if not format_url or mimetype == 'video/mp2t':
                continue

            height = int_or_none(fmt.get('height'))
            render_type = str_or_none(fmt.get('render_type'))
            format_id = f'{render_type}-{height}p' if render_type and height else None

            # Veo returns panoramic video information even if panoramic video is not available.
            # e.g. https://app.veo.co/matches/20201027-last-period/
            if render_type == 'panorama':
                if not self._is_valid_url(format_url, video_id, format_id):
                    continue

            formats.append({
                'url': format_url,
                'format_id': format_id,
                'ext': mimetype2ext(mimetype),
                'width': int_or_none(fmt.get('width')),
                'height': height,
                'vbr': int_or_none(fmt.get('bit_rate'), scale=1000),
            })

        self._sort_formats(formats)

        return {
            'id': video_id,
            'title': str_or_none(metadata.get('title')),
            'formats': formats,
            'thumbnail': url_or_none(metadata.get('thumbnail')),
            'timestamp': unified_timestamp(metadata.get('created')),
            'view_count': int_or_none(metadata.get('view_count')),
            'duration': int_or_none(metadata.get('duration')),
        }


code:
from .common import InfoExtractor
from ..utils import (
    int_or_none,
    parse_duration,
    qualities,
    try_get
)

class VeohIE(InfoExtractor):
    _VALID_URL = r'https?://(?:www\.)?veoh\.com/(?:watch|videos|embed|iphone/#_Watch)/(?P<id>(?:v|e|yapi-)[\da-zA-Z]+)'

    _TESTS = [{
        'url': 'http://www.veoh.com/watch/v56314296nk7Zdmz3',
        'md5': '620e68e6a3cff80086df3348426c9ca3',
        'info_dict': {
            'id': 'v56314296nk7Zdmz3',
            'ext': 'mp4',
            'title': 'Straight Backs Are Stronger',
            'description': 'md5:203f976279939a6dc664d4001e13f5f4',
            'thumbnail': 're:https://fcache\\.veoh\\.com/file/f/th56314296\\.jpg(\\?.*)?',
            'uploader': 'LUMOback',
            'duration': 46,
            'view_count': int,
            'average_rating': int,
            'comment_count': int,
            'age_limit': 0,
            'categories': ['technology_and_gaming'],
            'tags': ['posture', 'posture', 'sensor', 'back', 'pain', 'wearable', 'tech', 'lumo'],
        },
    }, {
        'url': 'http://www.veoh.com/embed/v56314296nk7Zdmz3',
        'only_matching': True,
    }, {
        'url': 'http://www.veoh.com/watch/v27701988pbTc4wzN?h1=Chile+workers+cover+up+to+avoid+skin+damage',
        'md5': '4a6ff84b87d536a6a71e6aa6c0ad07fa',
        'info_dict': {
            'id': '27701988',
            'ext': 'mp4',
            'title': 'Chile workers cover up to avoid skin damage',
            'description': 'md5:2bd151625a60a32822873efc246ba20d',
            'uploader': 'afp-news',
            'duration': 123,
        },
        'skip': 'This video has been deleted.',
    }, {
        'url': 'http://www.veoh.com/watch/v69525809F6Nc4frX',
        'md5': '4fde7b9e33577bab2f2f8f260e30e979',
        'note': 'Embedded ooyala video',
        'info_dict': {
            'id': '69525809',
            'ext': 'mp4',
            'title': 'Doctors Alter Plan For Preteen\'s Weight Loss Surgery',
            'description': 'md5:f5a11c51f8fb51d2315bca0937526891',
            'uploader': 'newsy-videos',
        },
        'skip': 'This video has been deleted.',
    }, {
        'url': 'http://www.veoh.com/watch/e152215AJxZktGS',
        'only_matching': True,
    }, {
        'url': 'https://www.veoh.com/videos/v16374379WA437rMH',
        'md5': 'cceb73f3909063d64f4b93d4defca1b3',
        'info_dict': {
            'id': 'v16374379WA437rMH',
            'ext': 'mp4',
            'title': 'Phantasmagoria 2, pt. 1-3',
            'description': 'Phantasmagoria: a Puzzle of Flesh',
            'thumbnail': 're:https://fcache\\.veoh\\.com/file/f/th16374379\\.jpg(\\?.*)?',
            'uploader': 'davidspackage',
            'duration': 968,
            'view_count': int,
            'average_rating': int,
            'comment_count': int,
            'age_limit': 18,
            'categories': ['technology_and_gaming', 'gaming'],
            'tags': ['puzzle', 'of', 'flesh'],
        }
    }]

    def _real_extract(self, url):
        video_id = self._match_id(url)
        metadata = self._download_json(
            'https://www.veoh.com/watch/getVideo/' + video_id,
            video_id)
        video = metadata['video']
        title = video['title']

        thumbnail_url = None
        q = qualities(['Regular', 'HQ'])
        formats = []
        for f_id, f_url in video.get('src', {}).items():
            if not f_url:
                continue
            if f_id == 'poster':
                thumbnail_url = f_url
            else:
                formats.append({
                    'format_id': f_id,
                    'quality': q(f_id),
                    'url': f_url,
                })
        self._sort_formats(formats)

        categories = metadata.get('categoryPath')
        if not categories:
            category = try_get(video, lambda x: x['category'].strip().removeprefix('category_'))
            categories = [category] if category else None
        tags = video.get('tags')

        return {
            'id': video_id,
            'title': title,
            'description': video.get('description'),
            'thumbnail': thumbnail_url,
            'uploader': video.get('author', {}).get('nickname'),
            'duration': int_or_none(video.get('lengthBySec')) or parse_duration(video.get('length')),
            'view_count': int_or_none(video.get('views')),
            'formats': formats,
            'average_rating': int_or_none(video.get('rating')),
            'comment_count': int_or_none(video.get('numOfComments')),
            'age_limit': 18 if video.get('contentRatingId') == 2 else 0,
            'categories': categories,
            'tags': tags.split(', ') if tags else None,
        }


code:
import re

from .common import InfoExtractor
from ..utils import ExtractorError
from .rutv import RUTVIE

class VestiIE(InfoExtractor):
    IE_DESC = 'Вести.Ru'
    _VALID_URL = r'https?://(?:.+?\.)?vesti\.ru/(?P<id>.+)'

    _TESTS = [
        {
            'url': 'http://www.vesti.ru/videos?vid=575582&cid=1',
            'info_dict': {
                'id': '765035',
                'ext': 'mp4',
                'title': 'Вести.net: биткоины в России не являются законными',
                'description': 'md5:d4bb3859dc1177b28a94c5014c35a36b',
                'duration': 302,
            },
            'params': {
                # m3u8 download
                'skip_download': True,
            },
        },
        {
            'url': 'http://www.vesti.ru/doc.html?id=1349233',
            'info_dict': {
                'id': '773865',
                'ext': 'mp4',
                'title': 'Участники митинга штурмуют Донецкую областную администрацию',
                'description': 'md5:1a160e98b3195379b4c849f2f4958009',
                'duration': 210,
            },
            'params': {
                # m3u8 download
                'skip_download': True,
            },
        },
        {
            'url': 'http://www.vesti.ru/only_video.html?vid=576180',
            'info_dict': {
                'id': '766048',
                'ext': 'mp4',
                'title': 'США заморозило, Британию затопило',
                'description': 'md5:f0ed0695ec05aed27c56a70a58dc4cc1',
                'duration': 87,
            },
            'params': {
                # m3u8 download
                'skip_download': True,
            },
        },
        {
            'url': 'http://hitech.vesti.ru/news/view/id/4000',
            'info_dict': {
                'id': '766888',
                'ext': 'mp4',
                'title': 'Вести.net: интернет-гиганты начали перетягивание программных "одеял"',
                'description': 'md5:65ddd47f9830c4f42ed6475f8730c995',
                'duration': 279,
            },
            'params': {
                # m3u8 download
                'skip_download': True,
            },
        },
        {
            'url': 'http://sochi2014.vesti.ru/video/index/video_id/766403',
            'info_dict': {
                'id': '766403',
                'ext': 'mp4',
                'title': 'XXII зимние Олимпийские игры. Российские хоккеисты стартовали на Олимпиаде с победы',
                'description': 'md5:55805dfd35763a890ff50fa9e35e31b3',
                'duration': 271,
            },
            'params': {
                # m3u8 download
                'skip_download': True,
            },
            'skip': 'Blocked outside Russia',
        },
        {
            'url': 'http://sochi2014.vesti.ru/live/play/live_id/301',
            'info_dict': {
                'id': '51499',
                'ext': 'flv',
                'title': 'Сочи-2014. Биатлон. Индивидуальная гонка. Мужчины ',
                'description': 'md5:9e0ed5c9d2fa1efbfdfed90c9a6d179c',
            },
            'params': {
                # rtmp download
                'skip_download': True,
            },
            'skip': 'Translation has finished'
        },
    ]

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

        page = self._download_webpage(url, video_id, 'Downloading page')

        mobj = re.search(
            r'<meta[^>]+?property="og:video"[^>]+?content="http://www\.vesti\.ru/i/flvplayer_videoHost\.swf\?vid=(?P<id>\d+)',
            page)
        if mobj:
            video_id = mobj.group('id')
            page = self._download_webpage('http://www.vesti.ru/only_video.html?vid=%s' % video_id, video_id,
                                          'Downloading video page')

        rutv_url = RUTVIE._extract_url(page)
        if rutv_url:
            return self.url_result(rutv_url, 'RUTV')

        raise ExtractorError('No video found', expected=True)


code:
import re
import json

from .common import InfoExtractor
from ..compat import (
    compat_str,
    compat_HTTPError,
)
from ..utils import (
    ExtractorError,
    int_or_none,
    parse_iso8601,
    parse_qs,
)

class VevoBaseIE(InfoExtractor):
    def _extract_json(self, webpage, video_id):
        return self._parse_json(
            self._search_regex(
                r'window\.__INITIAL_STORE__\s*=\s*({.+?});\s*</script>',
                webpage, 'initial store'),
            video_id)

class VevoIE(VevoBaseIE):
    '''
    Accepts urls from vevo.com or in the format 'vevo:{id}'
    (currently used by MTVIE and MySpaceIE)
    '''
    _VALID_URL = r'''(?x)
        (?:https?://(?:www\.)?vevo\.com/watch/(?!playlist|genre)(?:[^/]+/(?:[^/]+/)?)?|
           https?://cache\.vevo\.com/m/html/embed\.html\?video=|
           https?://videoplayer\.vevo\.com/embed/embedded\?videoId=|
           https?://embed\.vevo\.com/.*?[?&]isrc=|
           https?://tv\.vevo\.com/watch/artist/(?:[^/]+)/|
           vevo:)
        (?P<id>[^&?#]+)'''
    _EMBED_REGEX = [r'<iframe[^>]+?src=(["\'])(?P<url>(?:https?:)?//(?:cache\.)?vevo\.com/.+?)\1']

    _TESTS = [{
        'url': 'http://www.vevo.com/watch/hurts/somebody-to-die-for/GB1101300280',
        'md5': '95ee28ee45e70130e3ab02b0f579ae23',
        'info_dict': {
            'id': 'GB1101300280',
            'ext': 'mp4',
            'title': 'Hurts - Somebody to Die For',
            'timestamp': 1372057200,
            'upload_date': '20130624',
            'uploader': 'Hurts',
            'track': 'Somebody to Die For',
            'artist': 'Hurts',
            'genre': 'Pop',
        },
        'expected_warnings': ['Unable to download SMIL file', 'Unable to download info'],
    }, {
        'note': 'v3 SMIL format',
        'url': 'http://www.vevo.com/watch/cassadee-pope/i-wish-i-could-break-your-heart/USUV71302923',
        'md5': 'f6ab09b034f8c22969020b042e5ac7fc',
        'info_dict': {
            'id': 'USUV71302923',
            'ext': 'mp4',
            'title': 'Cassadee Pope - I Wish I Could Break Your Heart',
            'timestamp': 1392796919,
            'upload_date': '20140219',
            'uploader': 'Cassadee Pope',
            'track': 'I Wish I Could Break Your Heart',
            'artist': 'Cassadee Pope',
            'genre': 'Country',
        },
        'expected_warnings': ['Unable to download SMIL file', 'Unable to download info'],
    }, {
        'note': 'Age-limited video',
        'url': 'https://www.vevo.com/watch/justin-timberlake/tunnel-vision-explicit/USRV81300282',
        'info_dict': {
            'id': 'USRV81300282',
            'ext': 'mp4',
            'title': 'Justin Timberlake - Tunnel Vision (Explicit)',
            'age_limit': 18,
            'timestamp': 1372888800,
            'upload_date': '20130703',
            'uploader': 'Justin Timberlake',
            'track': 'Tunnel Vision (Explicit)',
            'artist': 'Justin Timberlake',
            'genre': 'Pop',
        },
        'expected_warnings': ['Unable to download SMIL file', 'Unable to download info'],
    }, {
        'note': 'No video_info',
        'url': 'http://www.vevo.com/watch/k-camp-1/Till-I-Die/USUV71503000',
        'md5': '8b83cc492d72fc9cf74a02acee7dc1b0',
        'info_dict': {
            'id': 'USUV71503000',
            'ext': 'mp4',
            'title': 'K Camp ft. T.I. - Till I Die',
            'age_limit': 18,
            'timestamp': 1449468000,
            'upload_date': '20151207',
            'uploader': 'K Camp',
            'track': 'Till I Die',
            'artist': 'K Camp',
            'genre': 'Hip-Hop',
        },
        'expected_warnings': ['Unable to download SMIL file', 'Unable to download info'],
    }, {
        'note': 'Featured test',
        'url': 'https://www.vevo.com/watch/lemaitre/Wait/USUV71402190',
        'md5': 'd28675e5e8805035d949dc5cf161071d',
        'info_dict': {
            'id': 'USUV71402190',
            'ext': 'mp4',
            'title': 'Lemaitre ft. LoLo - Wait',
            'age_limit': 0,
            'timestamp': 1413432000,
            'upload_date': '20141016',
            'uploader': 'Lemaitre',
            'track': 'Wait',
            'artist': 'Lemaitre',
            'genre': 'Electronic',
        },
        'expected_warnings': ['Unable to download SMIL file', 'Unable to download info'],
    }, {
        'note': 'Only available via webpage',
        'url': 'http://www.vevo.com/watch/GBUV71600656',
        'md5': '67e79210613865b66a47c33baa5e37fe',
        'info_dict': {
            'id': 'GBUV71600656',
            'ext': 'mp4',
            'title': 'ABC - Viva Love',
            'age_limit': 0,
            'timestamp': 1461830400,
            'upload_date': '20160428',
            'uploader': 'ABC',
            'track': 'Viva Love',
            'artist': 'ABC',
            'genre': 'Pop',
        },
        'expected_warnings': ['Failed to download video versions info'],
    }, {
        # no genres available
        'url': 'http://www.vevo.com/watch/INS171400764',
        'only_matching': True,
    }, {
        # Another case available only via the webpage; using streams/streamsV3 formats
        # Geo-restricted to Netherlands/Germany
        'url': 'http://www.vevo.com/watch/boostee/pop-corn-clip-officiel/FR1A91600909',
        'only_matching': True,
    }, {
        'url': 'https://embed.vevo.com/?isrc=USH5V1923499&partnerId=4d61b777-8023-4191-9ede-497ed6c24647&partnerAdCode=',
        'only_matching': True,
    }, {
        'url': 'https://tv.vevo.com/watch/artist/janet-jackson/US0450100550',
        'only_matching': True,
    }]
    _VERSIONS = {
        0: 'youtube',  # only in AuthenticateVideo videoVersions
        1: 'level3',
        2: 'akamai',
        3: 'level3',
        4: 'amazon',
    }

    def _initialize_api(self, video_id):
        webpage = self._download_webpage(
            'https://accounts.vevo.com/token', None,
            note='Retrieving oauth token',
            errnote='Unable to retrieve oauth token',
            data=json.dumps({
                'client_id': 'SPupX1tvqFEopQ1YS6SS',
                'grant_type': 'urn:vevo:params:oauth:grant-type:anonymous',
            }).encode('utf-8'),
            headers={
                'Content-Type': 'application/json',
            })

        if re.search(r'(?i)THIS PAGE IS CURRENTLY UNAVAILABLE IN YOUR REGION', webpage):
            self.raise_geo_restricted(
                '%s said: This page is currently unavailable in your region' % self.IE_NAME)

        auth_info = self._parse_json(webpage, video_id)
        self._api_url_template = self.http_scheme() + '//apiv2.vevo.com/%s?token=' + auth_info['legacy_token']

    def _call_api(self, path, *args, **kwargs):
        try:
            data = self._download_json(self._api_url_template % path, *args, **kwargs)
        except ExtractorError as e:
            if isinstance(e.cause, compat_HTTPError):
                errors = self._parse_json(e.cause.read().decode(), None)['errors']
                error_message = ', '.join([error['message'] for error in errors])
                raise ExtractorError('%s said: %s' % (self.IE_NAME, error_message), expected=True)
            raise
        return data

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

        self._initialize_api(video_id)

        video_info = self._call_api(
            'video/%s' % video_id, video_id, 'Downloading api video info',
            'Failed to download video info')

        video_versions = self._call_api(
            'video/%s/streams' % video_id, video_id,
            'Downloading video versions info',
            'Failed to download video versions info',
            fatal=False)

        # Some videos are only available via webpage (e.g.
        # https://github.com/ytdl-org/youtube-dl/issues/9366)
        if not video_versions:
            webpage = self._download_webpage(url, video_id)
            json_data = self._extract_json(webpage, video_id)
            if 'streams' in json_data.get('default', {}):
                video_versions = json_data['default']['streams'][video_id][0]
            else:
                video_versions = [
                    value
                    for key, value in json_data['apollo']['data'].items()
                    if key.startswith('%s.streams' % video_id)]

        uploader = None
        artist = None
        featured_artist = None
        artists = video_info.get('artists')
        for curr_artist in artists:
            if curr_artist.get('role') == 'Featured':
                featured_artist = curr_artist['name']
            else:
                artist = uploader = curr_artist['name']

        formats = []
        for video_version in video_versions:
            version = self._VERSIONS.get(video_version.get('version'), 'generic')
            version_url = video_version.get('url')
            if not version_url:
                continue

            if '.ism' in version_url:
                continue
            elif '.mpd' in version_url:
                formats.extend(self._extract_mpd_formats(
                    version_url, video_id, mpd_id='dash-%s' % version,
                    note='Downloading %s MPD information' % version,
                    errnote='Failed to download %s MPD information' % version,
                    fatal=False))
            elif '.m3u8' in version_url:
                formats.extend(self._extract_m3u8_formats(
                    version_url, video_id, 'mp4', 'm3u8_native',
                    m3u8_id='hls-%s' % version,
                    note='Downloading %s m3u8 information' % version,
                    errnote='Failed to download %s m3u8 information' % version,
                    fatal=False))
            else:
                m = re.search(r'''(?xi)
                    _(?P<quality>[a-z0-9]+)
                    _(?P<width>[0-9]+)x(?P<height>[0-9]+)
                    _(?P<vcodec>[a-z0-9]+)
                    _(?P<vbr>[0-9]+)
                    _(?P<acodec>[a-z0-9]+)
                    _(?P<abr>[0-9]+)
                    \.(?P<ext>[a-z0-9]+)''', version_url)
                if not m:
                    continue

                formats.append({
                    'url': version_url,
                    'format_id': f'http-{version}-{video_version.get("quality") or m.group("quality")}',
                    'vcodec': m.group('vcodec'),
                    'acodec': m.group('acodec'),
                    'vbr': int(m.group('vbr')),
                    'abr': int(m.group('abr')),
                    'ext': m.group('ext'),
                    'width': int(m.group('width')),
                    'height': int(m.group('height')),
                })
        self._sort_formats(formats)

        track = video_info['title']
        if featured_artist:
            artist = '%s ft. %s' % (artist, featured_artist)
        title = '%s - %s' % (artist, track) if artist else track

        genres = video_info.get('genres')
        genre = (
            genres[0] if genres and isinstance(genres, list)
            and isinstance(genres[0], compat_str) else None)

        is_explicit = video_info.get('isExplicit')
        if is_explicit is True:
            age_limit = 18
        elif is_explicit is False:
            age_limit = 0
        else:
            age_limit = None

        return {
            'id': video_id,
            'title': title,
            'formats': formats,
            'thumbnail': video_info.get('imageUrl') or video_info.get('thumbnailUrl'),
            'timestamp': parse_iso8601(video_info.get('releaseDate')),
            'uploader': uploader,
            'duration': int_or_none(video_info.get('duration')),
            'view_count': int_or_none(video_info.get('views', {}).get('total')),
            'age_limit': age_limit,
            'track': track,
            'artist': uploader,
            'genre': genre,
        }

class VevoPlaylistIE(VevoBaseIE):
    _VALID_URL = r'https?://(?:www\.)?vevo\.com/watch/(?P<kind>playlist|genre)/(?P<id>[^/?#&]+)'

    _TESTS = [{
        'url': 'http://www.vevo.com/watch/genre/rock',
        'info_dict': {
            'id': 'rock',
            'title': 'Rock',
        },
        'playlist_count': 20,
    }, {
        'url': 'http://www.vevo.com/watch/genre/rock?index=0',
        'only_matching': True,
    }]

    def _real_extract(self, url):
        mobj = self._match_valid_url(url)
        playlist_id = mobj.group('id')
        playlist_kind = mobj.group('kind')

        webpage = self._download_webpage(url, playlist_id)

        qs = parse_qs(url)
        index = qs.get('index', [None])[0]

        if index:
            video_id = self._search_regex(
                r'<meta[^>]+content=(["\'])vevo://video/(?P<id>.+?)\1[^>]*>',
                webpage, 'video id', default=None, group='id')
            if video_id:
                return self.url_result('vevo:%s' % video_id, VevoIE.ie_key())

        playlists = self._extract_json(webpage, playlist_id)['default']['%ss' % playlist_kind]

        playlist = (list(playlists.values())[0]
                    if playlist_kind == 'playlist' else playlists[playlist_id])

        entries = [
            self.url_result('vevo:%s' % src, VevoIE.ie_key())
            for src in playlist['isrcs']]

        return self.playlist_result(
            entries, playlist.get('playlistId') or playlist_id,
            playlist.get('name'), playlist.get('description'))


code:
import re

from .common import InfoExtractor
from .xstream import XstreamIE
from ..utils import (
    ExtractorError,
    float_or_none,
    try_get,
)

class VGTVIE(XstreamIE):
    IE_DESC = 'VGTV, BTTV, FTV, Aftenposten and Aftonbladet'
    _GEO_BYPASS = False

    _HOST_TO_APPNAME = {
        'tv.vg.no': 'vgtv',
        'vgtv.no': 'vgtv',
        'bt.no/tv': 'bttv',
        'aftenbladet.no/tv': 'satv',
        'fvn.no/fvntv': 'fvntv',
        'aftenposten.no/webtv': 'aptv',
        'ap.vgtv.no/webtv': 'aptv',
        'tv.aftonbladet.se': 'abtv',
        # obsolete URL schemas, kept in order to save one HTTP redirect
        'tv.aftonbladet.se/abtv': 'abtv',
        'www.aftonbladet.se/tv': 'abtv',
    }

    _APP_NAME_TO_VENDOR = {
        'vgtv': 'vgtv',
        'bttv': 'bt',
        'satv': 'sa',
        'fvntv': 'fvn',
        'aptv': 'ap',
        'abtv': 'ab',
    }

    _VALID_URL = r'''(?x)
                    (?:https?://(?:www\.)?
                    (?P<host>
                        %s
                    )
                    /?
                    (?:
                        (?:\#!/)?(?:video|live)/|
                        embed?.*id=|
                        a(?:rticles)?/
                    )|
                    (?P<appname>
                        %s
                    ):)
                    (?P<id>\d+)
                    ''' % ('|'.join(_HOST_TO_APPNAME.keys()), '|'.join(_APP_NAME_TO_VENDOR.keys()))

    _TESTS = [
        {
            # streamType: vod
            'url': 'http://www.vgtv.no/#!/video/84196/hevnen-er-soet-episode-10-abu',
            'md5': 'b8be7a234cebb840c0d512c78013e02f',
            'info_dict': {
                'id': '84196',
                'ext': 'mp4',
                'title': 'Hevnen er søt: Episode 10 - Abu',
                'description': 'md5:e25e4badb5f544b04341e14abdc72234',
                'thumbnail': r're:^https?://.*\.jpg',
                'duration': 648.000,
                'timestamp': 1404626400,
                'upload_date': '20140706',
                'view_count': int,
            },
        },
        {
            # streamType: wasLive
            'url': 'http://www.vgtv.no/#!/live/100764/opptak-vgtv-foelger-em-kvalifiseringen',
            'info_dict': {
                'id': '100764',
                'ext': 'flv',
                'title': 'OPPTAK: VGTV følger EM-kvalifiseringen',
                'description': 'md5:3772d9c0dc2dff92a886b60039a7d4d3',
                'thumbnail': r're:^https?://.*\.jpg',
                'duration': 9103.0,
                'timestamp': 1410113864,
                'upload_date': '20140907',
                'view_count': int,
            },
            'params': {
                # m3u8 download
                'skip_download': True,
            },
            'skip': 'Video is no longer available',
        },
        {
            # streamType: wasLive
            'url': 'http://www.vgtv.no/#!/live/113063/direkte-v75-fra-solvalla',
            'info_dict': {
                'id': '113063',
                'ext': 'mp4',
                'title': 'V75 fra Solvalla 30.05.15',
                'description': 'md5:b3743425765355855f88e096acc93231',
                'thumbnail': r're:^https?://.*\.jpg',
                'duration': 25966,
                'timestamp': 1432975582,
                'upload_date': '20150530',
                'view_count': int,
            },
            'params': {
                # m3u8 download
                'skip_download': True,
            },
        },
        {
            'url': 'http://www.aftenposten.no/webtv/#!/video/21039/trailer-sweatshop-i-can-t-take-any-more',
            'md5': 'fd828cd29774a729bf4d4425fe192972',
            'info_dict': {
                'id': '21039',
                'ext': 'mp4',
                'title': 'TRAILER: «SWEATSHOP» - I can´t take any more',
                'description': 'md5:21891f2b0dd7ec2f78d84a50e54f8238',
                'duration': 66,
                'timestamp': 1417002452,
                'upload_date': '20141126',
                'view_count': int,
            },
            'params': {
                # m3u8 download
                'skip_download': True,
            },
        },
        {
            'url': 'https://tv.vg.no/video/241779/politiets-ekstremkjoering',
            'only_matching': True,
        },
        {
            'url': 'http://www.bt.no/tv/#!/video/100250/norling-dette-er-forskjellen-paa-1-divisjon-og-eliteserien',
            'only_matching': True,
        },
        {
            'url': 'http://ap.vgtv.no/webtv#!/video/111084/de-nye-bysyklene-lettere-bedre-gir-stoerre-hjul-og-feste-til-mobil',
            'only_matching': True,
        },
        {
            # geoblocked
            'url': 'http://www.vgtv.no/#!/video/127205/inside-the-mind-of-favela-funk',
            'only_matching': True,
        },
        {
            'url': 'https://tv.aftonbladet.se/video/36015/vulkanutbrott-i-rymden-nu-slapper-nasa-bilderna',
            'only_matching': True,
        },
        {
            'url': 'http://tv.aftonbladet.se/abtv/articles/36015',
            'only_matching': True,
        },
        {
            'url': 'https://www.aftonbladet.se/tv/a/36015',
            'only_matching': True,
        },
        {
            'url': 'abtv:140026',
            'only_matching': True,
        },
        {
            'url': 'http://www.vgtv.no/video/84196/hevnen-er-soet-episode-10-abu',
            'only_matching': True,
        },
    ]

    def _real_extract(self, url):
        mobj = self._match_valid_url(url)
        video_id = mobj.group('id')
        host = mobj.group('host')
        appname = self._HOST_TO_APPNAME[host] if host else mobj.group('appname')
        vendor = self._APP_NAME_TO_VENDOR[appname]

        data = self._download_json(
            'http://svp.vg.no/svp/api/v1/%s/assets/%s?appName=%s-website'
            % (vendor, video_id, appname),
            video_id, 'Downloading media JSON')

        if data.get('status') == 'inactive':
            raise ExtractorError(
                'Video %s is no longer available' % video_id, expected=True)

        info = {
            'formats': [],
        }
        if len(video_id) == 5:
            if appname == 'bttv':
                info = self._extract_video_info('btno', video_id)

        streams = data['streamUrls']
        stream_type = data.get('streamType')
        is_live = stream_type == 'live'
        formats = []

        hls_url = streams.get('hls')
        if hls_url:
            formats.extend(self._extract_m3u8_formats(
                hls_url, video_id, 'mp4', live=is_live, m3u8_id='hls', fatal=False))

        hds_url = streams.get('hds')
        if hds_url:
            hdcore_sign = 'hdcore=3.7.0'
            f4m_formats = self._extract_f4m_formats(
                hds_url + '?%s' % hdcore_sign, video_id, f4m_id='hds', fatal=False)
            if f4m_formats:
                for entry in f4m_formats:
                    # URLs without the extra param induce an 404 error
                    entry.update({'extra_param_to_segment_url': hdcore_sign})
                    formats.append(entry)

        mp4_urls = streams.get('pseudostreaming') or []
        mp4_url = streams.get('mp4')
        if mp4_url:
            mp4_urls.append(mp4_url)
        for mp4_url in mp4_urls:
            format_info = {
                'url': mp4_url,
            }
            mobj = re.search(r'(\d+)_(\d+)_(\d+)', mp4_url)
            if mobj:
                tbr = int(mobj.group(3))
                format_info.update({
                    'width': int(mobj.group(1)),
                    'height': int(mobj.group(2)),
                    'tbr': tbr,
                    'format_id': 'mp4-%s' % tbr,
                })
            formats.append(format_info)

        info['formats'].extend(formats)

        if not info['formats']:
            properties = try_get(
                data, lambda x: x['streamConfiguration']['properties'], list)
            if properties and 'geoblocked' in properties:
                raise self.raise_geo_restricted(
                    countries=[host.rpartition('.')[-1].partition('/')[0].upper()])

        self._sort_formats(info['formats'])

        info.update({
            'id': video_id,
            'title': data['title'],
            'description': data['description'],
            'thumbnail': data['images']['main'] + '?t[]=900x506q80',
            'timestamp': data['published'],
            'duration': float_or_none(data['duration'], 1000),
            'view_count': data['displays'],
            'is_live': is_live,
        })
        return info

class BTArticleIE(InfoExtractor):
    IE_NAME = 'bt:article'
    IE_DESC = 'Bergens Tidende Articles'
    _VALID_URL = r'https?://(?:www\.)?bt\.no/(?:[^/]+/)+(?P<id>[^/]+)-\d+\.html'
    _TEST = {
        'url': 'http://www.bt.no/nyheter/lokalt/Kjemper-for-internatet-1788214.html',
        'md5': '2acbe8ad129b3469d5ae51b1158878df',
        'info_dict': {
            'id': '23199',
            'ext': 'mp4',
            'title': 'Alrekstad internat',
            'description': 'md5:dc81a9056c874fedb62fc48a300dac58',
            'thumbnail': r're:^https?://.*\.jpg',
            'duration': 191,
            'timestamp': 1289991323,
            'upload_date': '20101117',
            'view_count': int,
        },
    }

    def _real_extract(self, url):
        webpage = self._download_webpage(url, self._match_id(url))
        video_id = self._search_regex(
            r'<video[^>]+data-id="(\d+)"', webpage, 'video id')
        return self.url_result('bttv:%s' % video_id, 'VGTV')

class BTVestlendingenIE(InfoExtractor):
    IE_NAME = 'bt:vestlendingen'
    IE_DESC = 'Bergens Tidende - Vestlendingen'
    _VALID_URL = r'https?://(?:www\.)?bt\.no/spesial/vestlendingen/#!/(?P<id>\d+)'
    _TESTS = [{
        'url': 'http://www.bt.no/spesial/vestlendingen/#!/86588',
        'md5': 'd7d17e3337dc80de6d3a540aefbe441b',
        'info_dict': {
            'id': '86588',
            'ext': 'mov',
            'title': 'Otto Wollertsen',
            'description': 'Vestlendingen Otto Fredrik Wollertsen',
            'timestamp': 1430473209,
            'upload_date': '20150501',
        },
        'skip': '404 Error',
    }, {
        'url': 'http://www.bt.no/spesial/vestlendingen/#!/86255',
        'md5': 'a2893f8632e96389f4bdf36aa9463ceb',
        'info_dict': {
            'id': '86255',
            'ext': 'mov',
            'title': 'Du må tåle å fryse og være sulten',
            'description': 'md5:b8046f4d022d5830ddab04865791d063',
            'upload_date': '20150321',
            'timestamp': 1426942023,
        },
    }]

    def _real_extract(self, url):
        return self.url_result('bttv:%s' % self._match_id(url), 'VGTV')


code:
from .mtv import MTVServicesInfoExtractor

# TODO Remove - Reason: Outdated Site

class VH1IE(MTVServicesInfoExtractor):
    IE_NAME = 'vh1.com'
    _FEED_URL = 'http://www.vh1.com/feeds/mrss/'
    _TESTS = [{
        'url': 'https://www.vh1.com/episodes/0aqivv/nick-cannon-presents-wild-n-out-foushee-season-16-ep-12',
        'info_dict': {
            'title': 'Fousheé',
            'description': 'Fousheé joins Team Evolutions fight against Nick and Team Revolution in Baby Daddy, Baby Mama; Kick Em Out the Classroom; Backseat of My Ride and Wildstyle; and Fousheé performs.',
        },
        'playlist_mincount': 4,
        'skip': '404 Not found',
    }, {
        # Clip
        'url': 'https://www.vh1.com/video-clips/e0sja0/nick-cannon-presents-wild-n-out-foushee-clap-for-him',
        'info_dict': {
            'id': 'a07563f7-a37b-4e7f-af68-85855c2c7cc3',
            'ext': 'mp4',
            'title': 'Fousheé - "clap for him"',
            'description': 'Singer Fousheé hits the Wild N Out: In the Dark stage with a performance of the tongue-in-cheek track "clap for him" from her 2021 album "time machine."',
            'upload_date': '20210826',
        },
        'params': {
            # m3u8 download
            'skip_download': True,
        },
    }]

    _VALID_URL = r'https?://(?:www\.)?vh1\.com/(?:video-clips|episodes)/(?P<id>[^/?#.]+)'