code:
import re
import itertools

from .common import InfoExtractor
from ..compat import (
    compat_str,
    compat_urlparse,
)
from ..utils import (
    find_xpath_attr,
    xpath_attr,
    xpath_with_ns,
    xpath_text,
    orderedSet,
    update_url_query,
    int_or_none,
    float_or_none,
    parse_iso8601,
    determine_ext,
)

class LivestreamIE(InfoExtractor):
    IE_NAME = 'livestream'
    _VALID_URL = r'https?://(?:new\.)?livestream\.com/(?:accounts/(?P<account_id>\d+)|(?P<account_name>[^/]+))/(?:events/(?P<event_id>\d+)|(?P<event_name>[^/]+))(?:/videos/(?P<id>\d+))?'
    _EMBED_REGEX = [r'<iframe[^>]+src="(?P<url>https?://(?:new\.)?livestream\.com/[^"]+/player[^"]+)"']

    _TESTS = [{
        'url': 'http://new.livestream.com/CoheedandCambria/WebsterHall/videos/4719370',
        'md5': '53274c76ba7754fb0e8d072716f2292b',
        'info_dict': {
            'id': '4719370',
            'ext': 'mp4',
            'title': 'Live from Webster Hall NYC',
            'timestamp': 1350008072,
            'upload_date': '20121012',
            'duration': 5968.0,
            'like_count': int,
            'view_count': int,
            'thumbnail': r're:^http://.*\.jpg$'
        }
    }, {
        'url': 'http://new.livestream.com/tedx/cityenglish',
        'info_dict': {
            'title': 'TEDCity2.0 (English)',
            'id': '2245590',
        },
        'playlist_mincount': 4,
    }, {
        'url': 'http://new.livestream.com/chess24/tatasteelchess',
        'info_dict': {
            'title': 'Tata Steel Chess',
            'id': '3705884',
        },
        'playlist_mincount': 60,
    }, {
        'url': 'https://new.livestream.com/accounts/362/events/3557232/videos/67864563/player?autoPlay=false&height=360&mute=false&width=640',
        'only_matching': True,
    }, {
        'url': 'http://livestream.com/bsww/concacafbeachsoccercampeonato2015',
        'only_matching': True,
    }]
    _API_URL_TEMPLATE = 'http://livestream.com/api/accounts/%s/events/%s'

    def _parse_smil_formats(self, smil, smil_url, video_id, namespace=None, f4m_params=None, transform_rtmp_url=None):
        base_ele = find_xpath_attr(
            smil, self._xpath_ns('.//meta', namespace), 'name', 'httpBase')
        base = base_ele.get('content') if base_ele is not None else 'http://livestreamvod-f.akamaihd.net/'

        formats = []
        video_nodes = smil.findall(self._xpath_ns('.//video', namespace))

        for vn in video_nodes:
            tbr = int_or_none(vn.attrib.get('system-bitrate'), 1000)
            furl = (
                update_url_query(compat_urlparse.urljoin(base, vn.attrib['src']), {
                    'v': '3.0.3',
                    'fp': 'WIN% 14,0,0,145',
                }))
            if 'clipBegin' in vn.attrib:
                furl += '&ssek=' + vn.attrib['clipBegin']
            formats.append({
                'url': furl,
                'format_id': 'smil_%d' % tbr,
                'ext': 'flv',
                'tbr': tbr,
                'preference': -1000,  # Strictly inferior than all other formats?
            })
        return formats

    def _extract_video_info(self, video_data):
        video_id = compat_str(video_data['id'])

        FORMAT_KEYS = (
            ('sd', 'progressive_url'),
            ('hd', 'progressive_url_hd'),
        )

        formats = []
        for format_id, key in FORMAT_KEYS:
            video_url = video_data.get(key)
            if video_url:
                ext = determine_ext(video_url)
                if ext == 'm3u8':
                    continue
                bitrate = int_or_none(self._search_regex(
                    r'(\d+)\.%s' % ext, video_url, 'bitrate', default=None))
                formats.append({
                    'url': video_url,
                    'format_id': format_id,
                    'tbr': bitrate,
                    'ext': ext,
                })

        smil_url = video_data.get('smil_url')
        if smil_url:
            formats.extend(self._extract_smil_formats(smil_url, video_id, fatal=False))

        m3u8_url = video_data.get('m3u8_url')
        if m3u8_url:
            formats.extend(self._extract_m3u8_formats(
                m3u8_url, video_id, 'mp4', 'm3u8_native',
                m3u8_id='hls', fatal=False))

        f4m_url = video_data.get('f4m_url')
        if f4m_url:
            formats.extend(self._extract_f4m_formats(
                f4m_url, video_id, f4m_id='hds', fatal=False))
        self._sort_formats(formats)

        comments = [{
            'author_id': comment.get('author_id'),
            'author': comment.get('author', {}).get('full_name'),
            'id': comment.get('id'),
            'text': comment['text'],
            'timestamp': parse_iso8601(comment.get('created_at')),
        } for comment in video_data.get('comments', {}).get('data', [])]

        return {
            'id': video_id,
            'formats': formats,
            'title': video_data['caption'],
            'description': video_data.get('description'),
            'thumbnail': video_data.get('thumbnail_url'),
            'duration': float_or_none(video_data.get('duration'), 1000),
            'timestamp': parse_iso8601(video_data.get('publish_at')),
            'like_count': video_data.get('likes', {}).get('total'),
            'comment_count': video_data.get('comments', {}).get('total'),
            'view_count': video_data.get('views'),
            'comments': comments,
        }

    def _extract_stream_info(self, stream_info):
        broadcast_id = compat_str(stream_info['broadcast_id'])
        is_live = stream_info.get('is_live')

        formats = []
        smil_url = stream_info.get('play_url')
        if smil_url:
            formats.extend(self._extract_smil_formats(smil_url, broadcast_id))

        m3u8_url = stream_info.get('m3u8_url')
        if m3u8_url:
            formats.extend(self._extract_m3u8_formats(
                m3u8_url, broadcast_id, 'mp4', 'm3u8_native',
                m3u8_id='hls', fatal=False))

        rtsp_url = stream_info.get('rtsp_url')
        if rtsp_url:
            formats.append({
                'url': rtsp_url,
                'format_id': 'rtsp',
            })
        self._sort_formats(formats)

        return {
            'id': broadcast_id,
            'formats': formats,
            'title': stream_info['stream_title'],
            'thumbnail': stream_info.get('thumbnail_url'),
            'is_live': is_live,
        }

    def _extract_event(self, event_data):
        event_id = compat_str(event_data['id'])
        account_id = compat_str(event_data['owner_account_id'])
        feed_root_url = self._API_URL_TEMPLATE % (account_id, event_id) + '/feed.json'

        stream_info = event_data.get('stream_info')
        if stream_info:
            return self._extract_stream_info(stream_info)

        last_video = None
        entries = []
        for i in itertools.count(1):
            if last_video is None:
                info_url = feed_root_url
            else:
                info_url = '{root}?&id={id}&newer=-1&type=video'.format(
                    root=feed_root_url, id=last_video)
            videos_info = self._download_json(
                info_url, event_id, 'Downloading page {0}'.format(i))['data']
            videos_info = [v['data'] for v in videos_info if v['type'] == 'video']
            if not videos_info:
                break
            for v in videos_info:
                v_id = compat_str(v['id'])
                entries.append(self.url_result(
                    'http://livestream.com/accounts/%s/events/%s/videos/%s' % (account_id, event_id, v_id),
                    'Livestream', v_id, v.get('caption')))
            last_video = videos_info[-1]['id']
        return self.playlist_result(entries, event_id, event_data['full_name'])

    def _real_extract(self, url):
        mobj = self._match_valid_url(url)
        video_id = mobj.group('id')
        event = mobj.group('event_id') or mobj.group('event_name')
        account = mobj.group('account_id') or mobj.group('account_name')
        api_url = self._API_URL_TEMPLATE % (account, event)
        if video_id:
            video_data = self._download_json(
                api_url + '/videos/%s' % video_id, video_id)
            return self._extract_video_info(video_data)
        else:
            event_data = self._download_json(api_url, video_id)
            return self._extract_event(event_data)

# The original version of Livestream uses a different system
class LivestreamOriginalIE(InfoExtractor):
    IE_NAME = 'livestream:original'
    _VALID_URL = r'''(?x)https?://original\.livestream\.com/
        (?P<user>[^/\?#]+)(?:/(?P<type>video|folder)
        (?:(?:\?.*?Id=|/)(?P<id>.*?)(&|$))?)?
        '''
    _TESTS = [{
        'url': 'http://original.livestream.com/dealbook/video?clipId=pla_8aa4a3f1-ba15-46a4-893b-902210e138fb',
        'info_dict': {
            'id': 'pla_8aa4a3f1-ba15-46a4-893b-902210e138fb',
            'ext': 'mp4',
            'title': 'Spark 1 (BitCoin) with Cameron Winklevoss & Tyler Winklevoss of Winklevoss Capital',
            'duration': 771.301,
            'view_count': int,
        },
    }, {
        'url': 'https://original.livestream.com/newplay/folder?dirId=a07bf706-d0e4-4e75-a747-b021d84f2fd3',
        'info_dict': {
            'id': 'a07bf706-d0e4-4e75-a747-b021d84f2fd3',
        },
        'playlist_mincount': 4,
    }, {
        # live stream
        'url': 'http://original.livestream.com/znsbahamas',
        'only_matching': True,
    }]

    def _extract_video_info(self, user, video_id):
        api_url = 'http://x%sx.api.channel.livestream.com/2.0/clipdetails?extendedInfo=true&id=%s' % (user, video_id)
        info = self._download_xml(api_url, video_id)

        item = info.find('channel').find('item')
        title = xpath_text(item, 'title')
        media_ns = {'media': 'http://search.yahoo.com/mrss'}
        thumbnail_url = xpath_attr(
            item, xpath_with_ns('media:thumbnail', media_ns), 'url')
        duration = float_or_none(xpath_attr(
            item, xpath_with_ns('media:content', media_ns), 'duration'))
        ls_ns = {'ls': 'http://api.channel.livestream.com/2.0'}
        view_count = int_or_none(xpath_text(
            item, xpath_with_ns('ls:viewsCount', ls_ns)))

        return {
            'id': video_id,
            'title': title,
            'thumbnail': thumbnail_url,
            'duration': duration,
            'view_count': view_count,
        }

    def _extract_video_formats(self, video_data, video_id):
        formats = []

        progressive_url = video_data.get('progressiveUrl')
        if progressive_url:
            formats.append({
                'url': progressive_url,
                'format_id': 'http',
            })

        m3u8_url = video_data.get('httpUrl')
        if m3u8_url:
            formats.extend(self._extract_m3u8_formats(
                m3u8_url, video_id, 'mp4', 'm3u8_native',
                m3u8_id='hls', fatal=False))

        rtsp_url = video_data.get('rtspUrl')
        if rtsp_url:
            formats.append({
                'url': rtsp_url,
                'format_id': 'rtsp',
            })

        self._sort_formats(formats)
        return formats

    def _extract_folder(self, url, folder_id):
        webpage = self._download_webpage(url, folder_id)
        paths = orderedSet(re.findall(
            r'''(?x)(?:
                <li\s+class="folder">\s*<a\s+href="|
                <a\s+href="(?=https?://livestre\.am/)
            )([^"]+)"''', webpage))

        entries = [{
            '_type': 'url',
            'url': compat_urlparse.urljoin(url, p),
        } for p in paths]

        return self.playlist_result(entries, folder_id)

    def _real_extract(self, url):
        mobj = self._match_valid_url(url)
        user = mobj.group('user')
        url_type = mobj.group('type')
        content_id = mobj.group('id')
        if url_type == 'folder':
            return self._extract_folder(url, content_id)
        else:
            # this url is used on mobile devices
            stream_url = 'http://x%sx.api.channel.livestream.com/3.0/getstream.json' % user
            info = {}
            if content_id:
                stream_url += '?id=%s' % content_id
                info = self._extract_video_info(user, content_id)
            else:
                content_id = user
                webpage = self._download_webpage(url, content_id)
                info = {
                    'title': self._og_search_title(webpage),
                    'description': self._og_search_description(webpage),
                    'thumbnail': self._search_regex(r'channelLogo\.src\s*=\s*"([^"]+)"', webpage, 'thumbnail', None),
                }
            video_data = self._download_json(stream_url, content_id)
            is_live = video_data.get('isLive')
            info.update({
                'id': content_id,
                'title': info['title'],
                'formats': self._extract_video_formats(video_data, content_id),
                'is_live': is_live,
            })
            return info

# The server doesn't support HEAD request, the generic extractor can't detect
# the redirection
class LivestreamShortenerIE(InfoExtractor):
    IE_NAME = 'livestream:shortener'
    IE_DESC = False  # Do not list
    _VALID_URL = r'https?://livestre\.am/(?P<id>.+)'

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

        return self.url_result(self._og_search_url(webpage))


code:
from .common import InfoExtractor
from ..utils import format_field, traverse_obj, unified_timestamp

class LivestreamfailsIE(InfoExtractor):
    _VALID_URL = r'https?://(?:www\.)?livestreamfails\.com/clip/(?P<id>[0-9]+)'
    _TESTS = [{
        'url': 'https://livestreamfails.com/clip/139200',
        'md5': '8a03aea1a46e94a05af6410337463102',
        'info_dict': {
            'id': '139200',
            'ext': 'mp4',
            'display_id': 'ConcernedLitigiousSalmonPeteZaroll-O8yo9W2L8OZEKhV2',
            'title': 'Streamer jumps off a trampoline at full speed',
            'creator': 'paradeev1ch',
            'thumbnail': r're:^https?://.+',
            'timestamp': 1656271785,
            'upload_date': '20220626',
        }
    }]

    def _real_extract(self, url):
        video_id = self._match_id(url)
        api_response = self._download_json(f'https://api.livestreamfails.com/clip/{video_id}', video_id)

        return {
            'id': video_id,
            'display_id': api_response.get('sourceId'),
            'timestamp': unified_timestamp(api_response.get('createdAt')),
            'url': f'https://livestreamfails-video-prod.b-cdn.net/video/{api_response["videoId"]}',
            'title': api_response.get('label'),
            'creator': traverse_obj(api_response, ('streamer', 'label')),
            'thumbnail': format_field(api_response, 'imageId', 'https://livestreamfails-image-prod.b-cdn.net/image/%s')
        }

repo_name: Lesmiscore/ytdl


code:
from .common import InfoExtractor
from ..compat import compat_str
from ..utils import (
    clean_html,
    format_field,
    int_or_none,
    parse_iso8601,
    unified_strdate,
)

class LnkGoIE(InfoExtractor):
    _VALID_URL = r'https?://(?:www\.)?lnk(?:go)?\.(?:alfa\.)?lt/(?:visi-video/[^/]+|video)/(?P<id>[A-Za-z0-9-]+)(?:/(?P<episode_id>\d+))?'
    _TESTS = [{
        'url': 'http://www.lnkgo.lt/visi-video/aktualai-pratesimas/ziurek-putka-trys-klausimai',
        'info_dict': {
            'id': '10809',
            'ext': 'mp4',
            'title': "Put'ka: Trys Klausimai",
            'upload_date': '20161216',
            'description': 'Seniai matytas Put’ka užduoda tris klausimėlius. Pabandykime surasti atsakymus.',
            'age_limit': 18,
            'duration': 117,
            'thumbnail': r're:^https?://.*\.jpg$',
            'timestamp': 1481904000,
        },
        'params': {
            'skip_download': True,  # HLS download
        },
    }, {
        'url': 'http://lnkgo.alfa.lt/visi-video/aktualai-pratesimas/ziurek-nerdas-taiso-kompiuteri-2',
        'info_dict': {
            'id': '10467',
            'ext': 'mp4',
            'title': 'Nėrdas: Kompiuterio Valymas',
            'upload_date': '20150113',
            'description': 'md5:7352d113a242a808676ff17e69db6a69',
            'age_limit': 18,
            'duration': 346,
            'thumbnail': r're:^https?://.*\.jpg$',
            'timestamp': 1421164800,
        },
        'params': {
            'skip_download': True,  # HLS download
        },
    }, {
        'url': 'https://lnk.lt/video/neigalieji-tv-bokste/37413',
        'only_matching': True,
    }]
    _AGE_LIMITS = {
        'N-7': 7,
        'N-14': 14,
        'S': 18,
    }
    _M3U8_TEMPL = 'https://vod.lnk.lt/lnk_vod/lnk/lnk/%s:%s/playlist.m3u8%s'

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

        video_info = self._download_json(
            'https://lnk.lt/api/main/video-page/%s/%s/false' % (display_id, video_id or '0'),
            display_id)['videoConfig']['videoInfo']

        video_id = compat_str(video_info['id'])
        title = video_info['title']
        prefix = 'smil' if video_info.get('isQualityChangeAvailable') else 'mp4'
        formats = self._extract_m3u8_formats(
            self._M3U8_TEMPL % (prefix, video_info['videoUrl'], video_info.get('secureTokenParams') or ''),
            video_id, 'mp4', 'm3u8_native')
        self._sort_formats(formats)

        return {
            'id': video_id,
            'display_id': display_id,
            'title': title,
            'formats': formats,
            'thumbnail': format_field(video_info, 'posterImage', 'https://lnk.lt/all-images/%s'),
            'duration': int_or_none(video_info.get('duration')),
            'description': clean_html(video_info.get('htmlDescription')),
            'age_limit': self._AGE_LIMITS.get(video_info.get('pgRating'), 0),
            'timestamp': parse_iso8601(video_info.get('airDate')),
            'view_count': int_or_none(video_info.get('viewsCount')),
        }

class LnkIE(InfoExtractor):
    _VALID_URL = r'https?://(?:www\.)?lnk\.lt/[^/]+/(?P<id>\d+)'

    _TESTS = [{
        'url': 'https://lnk.lt/zinios/79791',
        'info_dict': {
            'id': '79791',
            'ext': 'mp4',
            'title': 'LNK.lt: Viešintų gyventojai sukilo prieš radijo bangų siųstuvą',
            'description': 'Svarbiausios naujienos trumpai, LNK žinios ir Info dienos pokalbiai.',
            'view_count': int,
            'duration': 233,
            'upload_date': '20191123',
            'thumbnail': r're:^https?://.*\.jpg$',
            'episode_number': 13431,
            'series': 'Naujausi žinių reportažai',
            'episode': 'Episode 13431'
        },
        'params': {'skip_download': True}
    }, {
        'url': 'https://lnk.lt/istorijos-trumpai/152546',
        'info_dict': {
            'id': '152546',
            'ext': 'mp4',
            'title': 'Radžio koncertas gaisre ',
            'description': 'md5:0666b5b85cb9fc7c1238dec96f71faba',
            'view_count': int,
            'duration': 54,
            'upload_date': '20220105',
            'thumbnail': r're:^https?://.*\.jpg$',
            'episode_number': 1036,
            'series': 'Istorijos trumpai',
            'episode': 'Episode 1036'
        },
        'params': {'skip_download': True}
    }, {
        'url': 'https://lnk.lt/gyvunu-pasaulis/151549',
        'info_dict': {
            'id': '151549',
            'ext': 'mp4',
            'title': 'Gyvūnų pasaulis',
            'description': '',
            'view_count': int,
            'duration': 1264,
            'upload_date': '20220108',
            'thumbnail': r're:^https?://.*\.jpg$',
            'episode_number': 16,
            'series': 'Gyvūnų pasaulis',
            'episode': 'Episode 16'
        },
        'params': {'skip_download': True}
    }]

    def _real_extract(self, url):
        id = self._match_id(url)
        video_json = self._download_json(f'https://lnk.lt/api/video/video-config/{id}', id)['videoInfo']
        formats, subtitles = [], {}
        if video_json.get('videoUrl'):
            fmts, subs = self._extract_m3u8_formats_and_subtitles(video_json['videoUrl'], id)
            formats.extend(fmts)
            subtitles = self._merge_subtitles(subtitles, subs)
        if video_json.get('videoFairplayUrl') and not video_json.get('drm'):
            fmts, subs = self._extract_m3u8_formats_and_subtitles(video_json['videoFairplayUrl'], id)
            formats.extend(fmts)
            subtitles = self._merge_subtitles(subtitles, subs)

        self._sort_formats(formats)
        return {
            'id': id,
            'title': video_json.get('title'),
            'description': video_json.get('description'),
            'view_count': video_json.get('viewsCount'),
            'duration': video_json.get('duration'),
            'upload_date': unified_strdate(video_json.get('airDate')),
            'thumbnail': format_field(video_json, 'posterImage', 'https://lnk.lt/all-images/%s'),
            'episode_number': int_or_none(video_json.get('episodeNumber')),
            'series': video_json.get('programTitle'),
            'formats': formats,
            'subtitles': subtitles,
        }


code:
from .common import InfoExtractor

class LocalNews8IE(InfoExtractor):
    _VALID_URL = r'https?://(?:www\.)?localnews8\.com/(?:[^/]+/)*(?P<display_id>[^/]+)/(?P<id>[0-9]+)'
    _TEST = {
        'url': 'http://www.localnews8.com/news/rexburg-business-turns-carbon-fiber-scraps-into-wedding-rings/35183304',
        'md5': 'be4d48aea61aa2bde7be2ee47691ad20',
        'info_dict': {
            'id': '35183304',
            'display_id': 'rexburg-business-turns-carbon-fiber-scraps-into-wedding-rings',
            'ext': 'mp4',
            'title': 'Rexburg business turns carbon fiber scraps into wedding ring',
            'description': 'The process was first invented by Lamborghini and less than a dozen companies around the world use it.',
            'duration': 153,
            'timestamp': 1441844822,
            'upload_date': '20150910',
            'uploader_id': 'api',
        }
    }

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

        webpage = self._download_webpage(url, display_id)

        partner_id = self._search_regex(
            r'partnerId\s*[:=]\s*(["\'])(?P<id>\d+)\1',
            webpage, 'partner id', group='id')
        kaltura_id = self._search_regex(
            r'videoIdString\s*[:=]\s*(["\'])kaltura:(?P<id>[0-9a-z_]+)\1',
            webpage, 'videl id', group='id')

        return {
            '_type': 'url_transparent',
            'url': 'kaltura:%s:%s' % (partner_id, kaltura_id),
            'ie_key': 'Kaltura',
            'id': video_id,
            'display_id': display_id,
        }


code:
from .nuevo import NuevoBaseIE

class LoveHomePornIE(NuevoBaseIE):
    _VALID_URL = r'https?://(?:www\.)?lovehomeporn\.com/video/(?P<id>\d+)(?:/(?P<display_id>[^/?#&]+))?'
    _TEST = {
        'url': 'http://lovehomeporn.com/video/48483/stunning-busty-brunette-girlfriend-sucking-and-riding-a-big-dick#menu',
        'info_dict': {
            'id': '48483',
            'display_id': 'stunning-busty-brunette-girlfriend-sucking-and-riding-a-big-dick',
            'ext': 'mp4',
            'title': 'Stunning busty brunette girlfriend sucking and riding a big dick',
            'age_limit': 18,
            'duration': 238.47,
        },
        'params': {
            'skip_download': True,
        }
    }

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

        info = self._extract_nuevo(
            'http://lovehomeporn.com/media/nuevo/config.php?key=%s' % video_id,
            video_id)
        info.update({
            'display_id': display_id,
            'age_limit': 18
        })
        return info


code:
from .common import InfoExtractor
from ..utils import (
    clean_html,
    merge_dicts,
    traverse_obj,
    url_or_none,
)

class LRTBaseIE(InfoExtractor):
    def _extract_js_var(self, webpage, var_name, default=None):
        return self._search_regex(
            fr'{var_name}\s*=\s*(["\'])((?:(?!\1).)+)\1',
            webpage, var_name.replace('_', ' '), default, group=2)

class LRTStreamIE(LRTBaseIE):
    _VALID_URL = r'https?://(?:www\.)?lrt\.lt/mediateka/tiesiogiai/(?P<id>[\w-]+)'
    _TESTS = [{
        'url': 'https://www.lrt.lt/mediateka/tiesiogiai/lrt-opus',
        'info_dict': {
            'id': 'lrt-opus',
            'live_status': 'is_live',
            'title': 're:^LRT Opus.+$',
            'ext': 'mp4'
        }
    }]

    def _real_extract(self, url):
        video_id = self._match_id(url)
        webpage = self._download_webpage(url, video_id)
        streams_data = self._download_json(self._extract_js_var(webpage, 'tokenURL'), video_id)

        formats, subtitles = [], {}
        for stream_url in traverse_obj(streams_data, (
                'response', 'data', lambda k, _: k.startswith('content')), expected_type=url_or_none):
            fmts, subs = self._extract_m3u8_formats_and_subtitles(stream_url, video_id, 'mp4', m3u8_id='hls', live=True)
            formats.extend(fmts)
            subtitles = self._merge_subtitles(subtitles, subs)
        self._sort_formats(formats)

        stream_title = self._extract_js_var(webpage, 'video_title', 'LRT')
        return {
            'id': video_id,
            'formats': formats,
            'subtitles': subtitles,
            'is_live': True,
            'title': f'{self._og_search_title(webpage)} - {stream_title}'
        }

class LRTVODIE(LRTBaseIE):
    _VALID_URL = r'https?://(?:www\.)?lrt\.lt(?P<path>/mediateka/irasas/(?P<id>[0-9]+))'
    _TESTS = [{
        # m3u8 download
        'url': 'https://www.lrt.lt/mediateka/irasas/2000127261/greita-ir-gardu-sicilijos-ikvepta-klasikiniu-makaronu-su-baklazanais-vakariene',
        'info_dict': {
            'id': '2000127261',
            'ext': 'mp4',
            'title': 'Greita ir gardu: Sicilijos įkvėpta klasikinių makaronų su baklažanais vakarienė',
            'description': 'md5:ad7d985f51b0dc1489ba2d76d7ed47fa',
            'duration': 3035,
            'timestamp': 1604079000,
            'upload_date': '20201030',
            'tags': ['LRT TELEVIZIJA', 'Beatos virtuvė', 'Beata Nicholson', 'Makaronai', 'Baklažanai', 'Vakarienė', 'Receptas'],
            'thumbnail': 'https://www.lrt.lt/img/2020/10/30/764041-126478-1287x836.jpg'
        },
    }, {
        # direct mp3 download
        'url': 'http://www.lrt.lt/mediateka/irasas/1013074524/',
        'md5': '389da8ca3cad0f51d12bed0c844f6a0a',
        'info_dict': {
            'id': '1013074524',
            'ext': 'mp3',
            'title': 'Kita tema 2016-09-05 15:05',
            'description': 'md5:1b295a8fc7219ed0d543fc228c931fb5',
            'duration': 3008,
            'view_count': int,
            'like_count': int,
        },
    }]

    def _real_extract(self, url):
        path, video_id = self._match_valid_url(url).groups()
        webpage = self._download_webpage(url, video_id)

        media_url = self._extract_js_var(webpage, 'main_url', path)
        media = self._download_json(self._extract_js_var(
            webpage, 'media_info_url',
            'https://www.lrt.lt/servisai/stream_url/vod/media_info/'),
            video_id, query={'url': media_url})
        jw_data = self._parse_jwplayer_data(
            media['playlist_item'], video_id, base_url=url)

        json_ld_data = self._search_json_ld(webpage, video_id)

        tags = []
        for tag in (media.get('tags') or []):
            tag_name = tag.get('name')
            if not tag_name:
                continue
            tags.append(tag_name)

        clean_info = {
            'description': clean_html(media.get('content')),
            'tags': tags,
        }

        return merge_dicts(clean_info, jw_data, json_ld_data)


code:
import re

from .common import InfoExtractor
from ..compat import (
    compat_str,
    compat_urlparse,
)
from ..utils import (
    ExtractorError,
    int_or_none,
    urlencode_postdata,
)

class LyndaBaseIE(InfoExtractor):
    _SIGNIN_URL = 'https://www.lynda.com/signin/lynda'
    _PASSWORD_URL = 'https://www.lynda.com/signin/password'
    _USER_URL = 'https://www.lynda.com/signin/user'
    _ACCOUNT_CREDENTIALS_HINT = 'Use --username and --password options to provide lynda.com account credentials.'
    _NETRC_MACHINE = 'lynda'

    @staticmethod
    def _check_error(json_string, key_or_keys):
        keys = [key_or_keys] if isinstance(key_or_keys, compat_str) else key_or_keys
        for key in keys:
            error = json_string.get(key)
            if error:
                raise ExtractorError('Unable to login: %s' % error, expected=True)

    def _perform_login_step(self, form_html, fallback_action_url, extra_form_data, note, referrer_url):
        action_url = self._search_regex(
            r'<form[^>]+action=(["\'])(?P<url>.+?)\1', form_html,
            'post url', default=fallback_action_url, group='url')

        if not action_url.startswith('http'):
            action_url = compat_urlparse.urljoin(self._SIGNIN_URL, action_url)

        form_data = self._hidden_inputs(form_html)
        form_data.update(extra_form_data)

        response = self._download_json(
            action_url, None, note,
            data=urlencode_postdata(form_data),
            headers={
                'Referer': referrer_url,
                'X-Requested-With': 'XMLHttpRequest',
            }, expected_status=(418, 500, ))

        self._check_error(response, ('email', 'password', 'ErrorMessage'))

        return response, action_url

    def _perform_login(self, username, password):
        # Step 1: download signin page
        signin_page = self._download_webpage(
            self._SIGNIN_URL, None, 'Downloading signin page')

        # Already logged in
        if any(re.search(p, signin_page) for p in (
                r'isLoggedIn\s*:\s*true', r'logout\.aspx', r'>Log out<')):
            return

        # Step 2: submit email
        signin_form = self._search_regex(
            r'(?s)(<form[^>]+data-form-name=["\']signin["\'][^>]*>.+?</form>)',
            signin_page, 'signin form')
        signin_page, signin_url = self._login_step(
            signin_form, self._PASSWORD_URL, {'email': username},
            'Submitting email', self._SIGNIN_URL)

        # Step 3: submit password
        password_form = signin_page['body']
        self._login_step(
            password_form, self._USER_URL, {'email': username, 'password': password},
            'Submitting password', signin_url)

class LyndaIE(LyndaBaseIE):
    IE_NAME = 'lynda'
    IE_DESC = 'lynda.com videos'
    _VALID_URL = r'''(?x)
                    https?://
                        (?:www\.)?(?:lynda\.com|educourse\.ga)/
                        (?:
                            (?:[^/]+/){2,3}(?P<course_id>\d+)|
                            player/embed
                        )/
                        (?P<id>\d+)
                    '''

    _TIMECODE_REGEX = r'\[(?P<timecode>\d+:\d+:\d+[\.,]\d+)\]'

    _TESTS = [{
        'url': 'https://www.lynda.com/Bootstrap-tutorials/Using-exercise-files/110885/114408-4.html',
        # md5 is unstable
        'info_dict': {
            'id': '114408',
            'ext': 'mp4',
            'title': 'Using the exercise files',
            'duration': 68
        }
    }, {
        'url': 'https://www.lynda.com/player/embed/133770?tr=foo=1;bar=g;fizz=rt&fs=0',
        'only_matching': True,
    }, {
        'url': 'https://educourse.ga/Bootstrap-tutorials/Using-exercise-files/110885/114408-4.html',
        'only_matching': True,
    }, {
        'url': 'https://www.lynda.com/de/Graphic-Design-tutorials/Willkommen-Grundlagen-guten-Gestaltung/393570/393572-4.html',
        'only_matching': True,
    }, {
        # Status="NotFound", Message="Transcript not found"
        'url': 'https://www.lynda.com/ASP-NET-tutorials/What-you-should-know/5034180/2811512-4.html',
        'only_matching': True,
    }]

    def _raise_unavailable(self, video_id):
        self.raise_login_required(
            'Video %s is only available for members' % video_id)

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

        query = {
            'videoId': video_id,
            'type': 'video',
        }

        video = self._download_json(
            'https://www.lynda.com/ajax/player', video_id,
            'Downloading video JSON', fatal=False, query=query)

        # Fallback scenario
        if not video:
            query['courseId'] = course_id

            play = self._download_json(
                'https://www.lynda.com/ajax/course/%s/%s/play'
                % (course_id, video_id), video_id, 'Downloading play JSON')

            if not play:
                self._raise_unavailable(video_id)

            formats = []
            for formats_dict in play:
                urls = formats_dict.get('urls')
                if not isinstance(urls, dict):
                    continue
                cdn = formats_dict.get('name')
                for format_id, format_url in urls.items():
                    if not format_url:
                        continue
                    formats.append({
                        'url': format_url,
                        'format_id': '%s-%s' % (cdn, format_id) if cdn else format_id,
                        'height': int_or_none(format_id),
                    })
            self._sort_formats(formats)

            conviva = self._download_json(
                'https://www.lynda.com/ajax/player/conviva', video_id,
                'Downloading conviva JSON', query=query)

            return {
                'id': video_id,
                'title': conviva['VideoTitle'],
                'description': conviva.get('VideoDescription'),
                'release_year': int_or_none(conviva.get('ReleaseYear')),
                'duration': int_or_none(conviva.get('Duration')),
                'creator': conviva.get('Author'),
                'formats': formats,
            }

        if 'Status' in video:
            raise ExtractorError(
                'lynda returned error: %s' % video['Message'], expected=True)

        if video.get('HasAccess') is False:
            self._raise_unavailable(video_id)

        video_id = compat_str(video.get('ID') or video_id)
        duration = int_or_none(video.get('DurationInSeconds'))
        title = video['Title']

        formats = []

        fmts = video.get('Formats')
        if fmts:
            formats.extend([{
                'url': f['Url'],
                'ext': f.get('Extension'),
                'width': int_or_none(f.get('Width')),
                'height': int_or_none(f.get('Height')),
                'filesize': int_or_none(f.get('FileSize')),
                'format_id': compat_str(f.get('Resolution')) if f.get('Resolution') else None,
            } for f in fmts if f.get('Url')])

        prioritized_streams = video.get('PrioritizedStreams')
        if prioritized_streams:
            for prioritized_stream_id, prioritized_stream in prioritized_streams.items():
                formats.extend([{
                    'url': video_url,
                    'height': int_or_none(format_id),
                    'format_id': '%s-%s' % (prioritized_stream_id, format_id),
                } for format_id, video_url in prioritized_stream.items()])

        self._check_formats(formats, video_id)
        self._sort_formats(formats)

        subtitles = self.extract_subtitles(video_id)

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

    def _fix_subtitles(self, subs):
        srt = ''
        seq_counter = 0
        for pos in range(0, len(subs) - 1):
            seq_current = subs[pos]
            m_current = re.match(self._TIMECODE_REGEX, seq_current['Timecode'])
            if m_current is None:
                continue
            seq_next = subs[pos + 1]
            m_next = re.match(self._TIMECODE_REGEX, seq_next['Timecode'])
            if m_next is None:
                continue
            appear_time = m_current.group('timecode')
            disappear_time = m_next.group('timecode')
            text = seq_current['Caption'].strip()
            if text:
                seq_counter += 1
                srt += '%s\r\n%s --> %s\r\n%s\r\n\r\n' % (seq_counter, appear_time, disappear_time, text)
        if srt:
            return srt

    def _get_subtitles(self, video_id):
        url = 'https://www.lynda.com/ajax/player?videoId=%s&type=transcript' % video_id
        subs = self._download_webpage(
            url, video_id, 'Downloading subtitles JSON', fatal=False)
        if not subs or 'Status="NotFound"' in subs:
            return {}
        subs = self._parse_json(subs, video_id, fatal=False)
        if not subs:
            return {}
        fixed_subs = self._fix_subtitles(subs)
        if fixed_subs:
            return {'en': [{'ext': 'srt', 'data': fixed_subs}]}
        return {}

class LyndaCourseIE(LyndaBaseIE):
    IE_NAME = 'lynda:course'
    IE_DESC = 'lynda.com online courses'

    # Course link equals to welcome/introduction video link of same course
    # We will recognize it as course link
    _VALID_URL = r'https?://(?:www|m)\.(?:lynda\.com|educourse\.ga)/(?P<coursepath>(?:[^/]+/){2,3}(?P<courseid>\d+))-2\.html'

    _TESTS = [{
        'url': 'https://www.lynda.com/Graphic-Design-tutorials/Grundlagen-guten-Gestaltung/393570-2.html',
        'only_matching': True,
    }, {
        'url': 'https://www.lynda.com/de/Graphic-Design-tutorials/Grundlagen-guten-Gestaltung/393570-2.html',
        'only_matching': True,
    }]

    def _real_extract(self, url):
        mobj = self._match_valid_url(url)
        course_path = mobj.group('coursepath')
        course_id = mobj.group('courseid')

        item_template = 'https://www.lynda.com/%s/%%s-4.html' % course_path

        course = self._download_json(
            'https://www.lynda.com/ajax/player?courseId=%s&type=course' % course_id,
            course_id, 'Downloading course JSON', fatal=False)

        if not course:
            webpage = self._download_webpage(url, course_id)
            entries = [
                self.url_result(
                    item_template % video_id, ie=LyndaIE.ie_key(),
                    video_id=video_id)
                for video_id in re.findall(
                    r'data-video-id=["\'](\d+)', webpage)]
            return self.playlist_result(
                entries, course_id,
                self._og_search_title(webpage, fatal=False),
                self._og_search_description(webpage))

        if course.get('Status') == 'NotFound':
            raise ExtractorError(
                'Course %s does not exist' % course_id, expected=True)

        unaccessible_videos = 0
        entries = []

        # Might want to extract videos right here from video['Formats'] as it seems 'Formats' is not provided
        # by single video API anymore

        for chapter in course['Chapters']:
            for video in chapter.get('Videos', []):
                if video.get('HasAccess') is False:
                    unaccessible_videos += 1
                    continue
                video_id = video.get('ID')
                if video_id:
                    entries.append({
                        '_type': 'url_transparent',
                        'url': item_template % video_id,
                        'ie_key': LyndaIE.ie_key(),
                        'chapter': chapter.get('Title'),
                        'chapter_number': int_or_none(chapter.get('ChapterIndex')),
                        'chapter_id': compat_str(chapter.get('ID')),
                    })

        if unaccessible_videos > 0:
            self.report_warning(
                '%s videos are only available for members (or paid members) and will not be downloaded. '
                % unaccessible_videos + self._ACCOUNT_CREDENTIALS_HINT)

        course_title = course.get('Title')
        course_description = course.get('Description')

        return self.playlist_result(entries, course_id, course_title, course_description)


code:
from .common import InfoExtractor

class M6IE(InfoExtractor):
    IE_NAME = 'm6'
    _VALID_URL = r'https?://(?:www\.)?m6\.fr/[^/]+/videos/(?P<id>\d+)-[^\.]+\.html'

    _TEST = {
        'url': 'http://www.m6.fr/emission-les_reines_du_shopping/videos/11323908-emeline_est_la_reine_du_shopping_sur_le_theme_ma_fete_d_8217_anniversaire.html',
        'md5': '242994a87de2c316891428e0176bcb77',
        'info_dict': {
            'id': '11323908',
            'ext': 'mp4',
            'title': 'Emeline est la Reine du Shopping sur le thème « Ma fête d’anniversaire ! »',
            'description': 'md5:1212ae8fb4b7baa4dc3886c5676007c2',
            'duration': 100,
        }
    }

    def _real_extract(self, url):
        video_id = self._match_id(url)
        return self.url_result('6play:%s' % video_id, 'SixPlay', video_id)


code:
from .common import InfoExtractor

class MagentaMusik360IE(InfoExtractor):
    _VALID_URL = r'https?://(?:www\.)?magenta-musik-360\.de/([a-z0-9-]+-(?P<id>[0-9]+)|festivals/.+)'
    _TESTS = [{
        'url': 'https://www.magenta-musik-360.de/within-temptation-wacken-2019-1-9208205928595185932',
        'md5': '65b6f060b40d90276ec6fb9b992c1216',
        'info_dict': {
            'id': '9208205928595185932',
            'ext': 'm3u8',
            'title': 'WITHIN TEMPTATION',
            'description': 'Robert Westerholt und Sharon Janny den Adel gründeten die Symphonic Metal-Band. Privat sind die Niederländer ein Paar und haben zwei Kinder. Die Single Ice Queen brachte ihnen Platin und Gold und verhalf 2002 zum internationalen Durchbruch. Charakteristisch für die Band war Anfangs der hohe Gesang von Frontfrau Sharon. Stilistisch fing die Band im Gothic Metal an. Mit neuem Sound, schnellen Gitarrenriffs und Gitarrensoli, avancierte Within Temptation zur erfolgreichen Rockband. Auch dieses Jahr wird die Band ihre Fangemeinde wieder mitreißen.',
        }
    }, {
        'url': 'https://www.magenta-musik-360.de/festivals/wacken-world-wide-2020-body-count-feat-ice-t',
        'md5': '81010d27d7cab3f7da0b0f681b983b7e',
        'info_dict': {
            'id': '9208205928595231363',
            'ext': 'm3u8',
            'title': 'Body Count feat. Ice-T',
            'description': 'Body Count feat. Ice-T konnten bereits im vergangenen Jahr auf dem „Holy Ground“ in Wacken überzeugen. 2020 gehen die Crossover-Metaller aus einem Club in Los Angeles auf Sendung und bringen mit ihrer Mischung aus Metal und Hip-Hop Abwechslung und ordentlich Alarm zum WWW. Bereits seit 1990 stehen die beiden Gründer Ice-T (Gesang) und Ernie C (Gitarre) auf der Bühne. Sieben Studioalben hat die Gruppe bis jetzt veröffentlicht, darunter das Debüt „Body Count“ (1992) mit dem kontroversen Track „Cop Killer“.',
        }
    }]

    def _real_extract(self, url):
        video_id = self._match_id(url)
        # _match_id casts to string, but since "None" is not a valid video_id for magenta
        # there is no risk for confusion
        if video_id == "None":
            webpage = self._download_webpage(url, video_id)
            video_id = self._html_search_regex(r'data-asset-id="([^"]+)"', webpage, 'video_id')
        json = self._download_json("https://wcps.t-online.de/cvss/magentamusic/vodplayer/v3/player/58935/%s/Main%%20Movie" % video_id, video_id)
        xml_url = json['content']['feature']['representations'][0]['contentPackages'][0]['media']['href']
        metadata = json['content']['feature'].get('metadata')
        title = None
        description = None
        duration = None
        thumbnails = []
        if metadata:
            title = metadata.get('title')
            description = metadata.get('fullDescription')
            duration = metadata.get('runtimeInSeconds')
            for img_key in ('teaserImageWide', 'smallCoverImage'):
                if img_key in metadata:
                    thumbnails.append({'url': metadata[img_key].get('href')})

        xml = self._download_xml(xml_url, video_id)
        final_url = xml[0][0][0].attrib['src']

        return {
            'id': video_id,
            'title': title,
            'description': description,
            'url': final_url,
            'duration': duration,
            'thumbnails': thumbnails
        }


code:
import itertools
import json
import re

from .common import InfoExtractor
from ..compat import compat_urllib_parse_unquote
from ..utils import (
    int_or_none,
    parse_duration,
    remove_end,
    try_get,
    urljoin,
)

class MailRuIE(InfoExtractor):
    IE_NAME = 'mailru'
    IE_DESC = 'Видео@Mail.Ru'
    _VALID_URL = r'''(?x)
                    https?://
                        (?:(?:www|m|videoapi)\.)?my\.mail\.ru/+
                        (?:
                            video/.*\#video=/?(?P<idv1>(?:[^/]+/){3}\d+)|
                            (?:videos/embed/)?(?:(?P<idv2prefix>(?:[^/]+/+){2})(?:video/(?:embed/)?)?(?P<idv2suffix>[^/]+/\d+))(?:\.html)?|
                            (?:video/embed|\+/video/meta)/(?P<metaid>\d+)
                        )
                    '''
    _TESTS = [
        {
            'url': 'http://my.mail.ru/video/top#video=/mail/sonypicturesrus/75/76',
            'md5': 'dea205f03120046894db4ebb6159879a',
            'info_dict': {
                'id': '46301138_76',
                'ext': 'mp4',
                'title': 'Новый Человек-Паук. Высокое напряжение. Восстание Электро',
                'timestamp': 1393235077,
                'upload_date': '20140224',
                'uploader': 'sonypicturesrus',
                'uploader_id': 'sonypicturesrus@mail.ru',
                'duration': 184,
            },
            'skip': 'Not accessible from Travis CI server',
        },
        {
            'url': 'http://my.mail.ru/corp/hitech/video/news_hi-tech_mail_ru/1263.html',
            'md5': '00a91a58c3402204dcced523777b475f',
            'info_dict': {
                'id': '46843144_1263',
                'ext': 'mp4',
                'title': 'Samsung Galaxy S5 Hammer Smash Fail Battery Explosion',
                'timestamp': 1397039888,
                'upload_date': '20140409',
                'uploader': 'hitech',
                'uploader_id': 'hitech@corp.mail.ru',
                'duration': 245,
            },
            'skip': 'Not accessible from Travis CI server',
        },
        {
            # only available via metaUrl API
            'url': 'http://my.mail.ru/mail/720pizle/video/_myvideo/502.html',
            'md5': '3b26d2491c6949d031a32b96bd97c096',
            'info_dict': {
                'id': '56664382_502',
                'ext': 'mp4',
                'title': ':8336',
                'timestamp': 1449094163,
                'upload_date': '20151202',
                'uploader': '720pizle@mail.ru',
                'uploader_id': '720pizle@mail.ru',
                'duration': 6001,
            },
            'skip': 'Not accessible from Travis CI server',
        },
        {
            'url': 'http://m.my.mail.ru/mail/3sktvtr/video/_myvideo/138.html',
            'only_matching': True,
        },
        {
            'url': 'https://my.mail.ru/video/embed/7949340477499637815',
            'only_matching': True,
        },
        {
            'url': 'http://my.mail.ru/+/video/meta/7949340477499637815',
            'only_matching': True,
        },
        {
            'url': 'https://my.mail.ru//list/sinyutin10/video/_myvideo/4.html',
            'only_matching': True,
        },
        {
            'url': 'https://my.mail.ru//list//sinyutin10/video/_myvideo/4.html',
            'only_matching': True,
        },
        {
            'url': 'https://my.mail.ru/mail/cloud-strife/video/embed/Games/2009',
            'only_matching': True,
        },
        {
            'url': 'https://videoapi.my.mail.ru/videos/embed/mail/cloud-strife/Games/2009.html',
            'only_matching': True,
        }
    ]

    def _real_extract(self, url):
        mobj = self._match_valid_url(url)
        meta_id = mobj.group('metaid')

        video_id = None
        if meta_id:
            meta_url = 'https://my.mail.ru/+/video/meta/%s' % meta_id
        else:
            video_id = mobj.group('idv1')
            if not video_id:
                video_id = mobj.group('idv2prefix') + mobj.group('idv2suffix')
            webpage = self._download_webpage(url, video_id)
            page_config = self._parse_json(self._search_regex([
                r'(?s)<script[^>]+class="sp-video__page-config"[^>]*>(.+?)</script>',
                r'(?s)"video":\s*({.+?}),'],
                webpage, 'page config', default='{}'), video_id, fatal=False)
            if page_config:
                meta_url = page_config.get('metaUrl') or page_config.get('video', {}).get('metaUrl') or page_config.get('metadataUrl')
            else:
                meta_url = None

        video_data = None

        # fix meta_url if missing the host address
        if re.match(r'^\/\+\/', meta_url):
            meta_url = urljoin('https://my.mail.ru', meta_url)

        if meta_url:
            video_data = self._download_json(
                meta_url, video_id or meta_id, 'Downloading video meta JSON',
                fatal=not video_id)

        # Fallback old approach
        if not video_data:
            video_data = self._download_json(
                'http://api.video.mail.ru/videos/%s.json?new=1' % video_id,
                video_id, 'Downloading video JSON')

        headers = {}

        video_key = self._get_cookies('https://my.mail.ru').get('video_key')
        if video_key:
            headers['Cookie'] = 'video_key=%s' % video_key.value

        formats = []
        for f in video_data['videos']:
            video_url = f.get('url')
            if not video_url:
                continue
            format_id = f.get('key')
            height = int_or_none(self._search_regex(
                r'^(\d+)[pP]$', format_id, 'height', default=None)) if format_id else None
            formats.append({
                'url': video_url,
                'format_id': format_id,
                'height': height,
                'http_headers': headers,
            })
        self._sort_formats(formats)

        meta_data = video_data['meta']
        title = remove_end(meta_data['title'], '.mp4')

        author = video_data.get('author')
        uploader = author.get('name')
        uploader_id = author.get('id') or author.get('email')
        view_count = int_or_none(video_data.get('viewsCount') or video_data.get('views_count'))

        acc_id = meta_data.get('accId')
        item_id = meta_data.get('itemId')
        content_id = '%s_%s' % (acc_id, item_id) if acc_id and item_id else video_id

        thumbnail = meta_data.get('poster')
        duration = int_or_none(meta_data.get('duration'))
        timestamp = int_or_none(meta_data.get('timestamp'))

        return {
            'id': content_id,
            'title': title,
            'thumbnail': thumbnail,
            'timestamp': timestamp,
            'uploader': uploader,
            'uploader_id': uploader_id,
            'duration': duration,
            'view_count': view_count,
            'formats': formats,
        }

class MailRuMusicSearchBaseIE(InfoExtractor):
    def _search(self, query, url, audio_id, limit=100, offset=0):
        search = self._download_json(
            'https://my.mail.ru/cgi-bin/my/ajax', audio_id,
            'Downloading songs JSON page %d' % (offset // limit + 1),
            headers={
                'Referer': url,
                'X-Requested-With': 'XMLHttpRequest',
            }, query={
                'xemail': '',
                'ajax_call': '1',
                'func_name': 'music.search',
                'mna': '',
                'mnb': '',
                'arg_query': query,
                'arg_extended': '1',
                'arg_search_params': json.dumps({
                    'music': {
                        'limit': limit,
                        'offset': offset,
                    },
                }),
                'arg_limit': limit,
                'arg_offset': offset,
            })
        return next(e for e in search if isinstance(e, dict))

    @staticmethod
    def _extract_track(t, fatal=True):
        audio_url = t['URL'] if fatal else t.get('URL')
        if not audio_url:
            return

        audio_id = t['File'] if fatal else t.get('File')
        if not audio_id:
            return

        thumbnail = t.get('AlbumCoverURL') or t.get('FiledAlbumCover')
        uploader = t.get('OwnerName') or t.get('OwnerName_Text_HTML')
        uploader_id = t.get('UploaderID')
        duration = int_or_none(t.get('DurationInSeconds')) or parse_duration(
            t.get('Duration') or t.get('DurationStr'))
        view_count = int_or_none(t.get('PlayCount') or t.get('PlayCount_hr'))

        track = t.get('Name') or t.get('Name_Text_HTML')
        artist = t.get('Author') or t.get('Author_Text_HTML')

        if track:
            title = '%s - %s' % (artist, track) if artist else track
        else:
            title = audio_id

        return {
            'extractor_key': MailRuMusicIE.ie_key(),
            'id': audio_id,
            'title': title,
            'thumbnail': thumbnail,
            'uploader': uploader,
            'uploader_id': uploader_id,
            'duration': duration,
            'view_count': view_count,
            'vcodec': 'none',
            'abr': int_or_none(t.get('BitRate')),
            'track': track,
            'artist': artist,
            'album': t.get('Album'),
            'url': audio_url,
        }

class MailRuMusicIE(MailRuMusicSearchBaseIE):
    IE_NAME = 'mailru:music'
    IE_DESC = 'Музыка@Mail.Ru'
    _VALID_URL = r'https?://my\.mail\.ru/+music/+songs/+[^/?#&]+-(?P<id>[\da-f]+)'
    _TESTS = [{
        'url': 'https://my.mail.ru/music/songs/%D0%BC8%D0%BB8%D1%82%D1%85-l-a-h-luciferian-aesthetics-of-herrschaft-single-2017-4e31f7125d0dfaef505d947642366893',
        'md5': '0f8c22ef8c5d665b13ac709e63025610',
        'info_dict': {
            'id': '4e31f7125d0dfaef505d947642366893',
            'ext': 'mp3',
            'title': 'L.A.H. (Luciferian Aesthetics of Herrschaft) single, 2017 - М8Л8ТХ',
            'uploader': 'Игорь Мудрый',
            'uploader_id': '1459196328',
            'duration': 280,
            'view_count': int,
            'vcodec': 'none',
            'abr': 320,
            'track': 'L.A.H. (Luciferian Aesthetics of Herrschaft) single, 2017',
            'artist': 'М8Л8ТХ',
        },
    }]

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

        webpage = self._download_webpage(url, audio_id)

        title = self._og_search_title(webpage)
        music_data = self._search(title, url, audio_id)['MusicData']
        t = next(t for t in music_data if t.get('File') == audio_id)

        info = self._extract_track(t)
        info['title'] = title
        return info

class MailRuMusicSearchIE(MailRuMusicSearchBaseIE):
    IE_NAME = 'mailru:music:search'
    IE_DESC = 'Музыка@Mail.Ru'
    _VALID_URL = r'https?://my\.mail\.ru/+music/+search/+(?P<id>[^/?#&]+)'
    _TESTS = [{
        'url': 'https://my.mail.ru/music/search/black%20shadow',
        'info_dict': {
            'id': 'black shadow',
        },
        'playlist_mincount': 532,
    }]

    def _real_extract(self, url):
        query = compat_urllib_parse_unquote(self._match_id(url))

        entries = []

        LIMIT = 100
        offset = 0

        for _ in itertools.count(1):
            search = self._search(query, url, query, LIMIT, offset)

            music_data = search.get('MusicData')
            if not music_data or not isinstance(music_data, list):
                break

            for t in music_data:
                track = self._extract_track(t, fatal=False)
                if track:
                    entries.append(track)

            total = try_get(
                search, lambda x: x['Results']['music']['Total'], int)

            if total is not None:
                if offset > total:
                    break

            offset += LIMIT

        return self.playlist_result(entries, query)


code:
import re

from .common import InfoExtractor

from ..utils import (
    int_or_none,
    js_to_json,
    parse_duration,
    traverse_obj,
    try_get,
    urljoin
)

class MainStreamingIE(InfoExtractor):
    _VALID_URL = r'https?://(?:webtools-?)?(?P<host>[A-Za-z0-9-]*\.msvdn.net)/(?:embed|amp_embed|content)/(?P<id>\w+)'
    _EMBED_REGEX = [rf'<iframe[^>]+?src=["\']?(?P<url>{_VALID_URL})["\']?']
    IE_DESC = 'MainStreaming Player'

    _TESTS = [
        {
            # Live stream offline, has alternative content id
            'url': 'https://webtools-e18da6642b684f8aa9ae449862783a56.msvdn.net/embed/53EN6GxbWaJC',
            'info_dict': {
                'id': '53EN6GxbWaJC',
                'title': 'Diretta homepage 2021-12-31 12:00',
                'description': '',
                'live_status': 'was_live',
                'ext': 'mp4',
                'thumbnail': r're:https?://[A-Za-z0-9-]*\.msvdn.net/image/\w+/poster',
            },
            'expected_warnings': [
                'Ignoring alternative content ID: WDAF1KOWUpH3',
                'MainStreaming said: Live event is OFFLINE'
            ],
            'skip': 'live stream offline'
        }, {
            # playlist
            'url': 'https://webtools-e18da6642b684f8aa9ae449862783a56.msvdn.net/embed/WDAF1KOWUpH3',
            'info_dict': {
                'id': 'WDAF1KOWUpH3',
                'title': 'Playlist homepage',
            },
            'playlist_mincount': 2
        }, {
            # livestream
            'url': 'https://webtools-859c1818ed614cc5b0047439470927b0.msvdn.net/embed/tDoFkZD3T1Lw',
            'info_dict': {
                'id': 'tDoFkZD3T1Lw',
                'title': r're:Class CNBC Live \d{4}-\d{2}-\d{2} \d{2}:\d{2}$',
                'live_status': 'is_live',
                'ext': 'mp4',
                'thumbnail': r're:https?://[A-Za-z0-9-]*\.msvdn.net/image/\w+/poster',
            },
            'skip': 'live stream'
        }, {
            'url': 'https://webtools-f5842579ff984c1c98d63b8d789673eb.msvdn.net/embed/EUlZfGWkGpOd?autoPlay=false',
            'info_dict': {
                'id': 'EUlZfGWkGpOd',
                'title': 'La Settimana ',
                'description': '03 Ottobre ore 02:00',
                'ext': 'mp4',
                'live_status': 'not_live',
                'thumbnail': r're:https?://[A-Za-z0-9-]*\.msvdn.net/image/\w+/poster',
                'duration': 1512
            }
        }, {
            # video without webtools- prefix
            'url': 'https://f5842579ff984c1c98d63b8d789673eb.msvdn.net/embed/MfuWmzL2lGkA?autoplay=false&T=1635860445',
            'info_dict': {
                'id': 'MfuWmzL2lGkA',
                'title': 'TG Mattina',
                'description': '06 Ottobre ore 08:00',
                'ext': 'mp4',
                'live_status': 'not_live',
                'thumbnail': r're:https?://[A-Za-z0-9-]*\.msvdn.net/image/\w+/poster',
                'duration': 789.04
            }
        }, {
            # always-on livestream with DVR
            'url': 'https://webtools-f5842579ff984c1c98d63b8d789673eb.msvdn.net/embed/HVvPMzy',
            'info_dict': {
                'id': 'HVvPMzy',
                'title': r're:^Diretta LaC News24 \d{4}-\d{2}-\d{2} \d{2}:\d{2}$',
                'description': 'canale all news',
                'live_status': 'is_live',
                'ext': 'mp4',
                'thumbnail': r're:https?://[A-Za-z0-9-]*\.msvdn.net/image/\w+/poster',
            },
            'params': {
                'skip_download': True,
            },
        }, {
            # no host
            'url': 'https://webtools.msvdn.net/embed/MfuWmzL2lGkA',
            'only_matching': True
        }, {
            'url': 'https://859c1818ed614cc5b0047439470927b0.msvdn.net/amp_embed/tDoFkZD3T1Lw',
            'only_matching': True
        }, {
            'url': 'https://859c1818ed614cc5b0047439470927b0.msvdn.net/content/tDoFkZD3T1Lw#',
            'only_matching': True
        }
    ]

    def _playlist_entries(self, host, playlist_content):
        for entry in playlist_content:
            content_id = entry.get('contentID')
            yield {
                '_type': 'url',
                'ie_key': MainStreamingIE.ie_key(),
                'id': content_id,
                'duration': int_or_none(traverse_obj(entry, ('duration', 'totalSeconds'))),
                'title': entry.get('title'),
                'url': f'https://{host}/embed/{content_id}'
            }

    @staticmethod
    def _get_webtools_host(host):
        if not host.startswith('webtools'):
            host = 'webtools' + ('-' if not host.startswith('.') else '') + host
        return host

    def _get_webtools_base_url(self, host):
        return f'{self.http_scheme()}//{self._get_webtools_host(host)}'

    def _call_api(self, host: str, path: str, item_id: str, query=None, note='Downloading API JSON', fatal=False):
        # JSON API, does not appear to be documented
        return self._call_webtools_api(host, '/api/v2/' + path, item_id, query, note, fatal)

    def _call_webtools_api(self, host: str, path: str, item_id: str, query=None, note='Downloading webtools API JSON', fatal=False):
        # webtools docs: https://webtools.msvdn.net/
        return self._download_json(
            urljoin(self._get_webtools_base_url(host), path), item_id, query=query, note=note, fatal=fatal)

    def _real_extract(self, url):
        host, video_id = self._match_valid_url(url).groups()
        content_info = try_get(
            self._call_api(
                host, f'content/{video_id}', video_id, note='Downloading content info API JSON'), lambda x: x['playerContentInfo'])
        # Fallback
        if not content_info:
            webpage = self._download_webpage(url, video_id)
            player_config = self._parse_json(
                self._search_regex(
                    r'config\s*=\s*({.+?})\s*;', webpage, 'mainstreaming player config',
                    default='{}', flags=re.DOTALL),
                video_id, transform_source=js_to_json, fatal=False) or {}
            content_info = player_config['contentInfo']

        host = content_info.get('host') or host
        video_id = content_info.get('contentID') or video_id
        title = content_info.get('title')
        description = traverse_obj(content_info, 'longDescription', 'shortDescription', expected_type=str)
        live_status = 'not_live'
        if content_info.get('drmEnabled'):
            self.report_drm(video_id)

        alternative_content_id = content_info.get('alternativeContentID')
        if alternative_content_id:
            self.report_warning(f'Ignoring alternative content ID: {alternative_content_id}')

        content_type = int_or_none(content_info.get('contentType'))
        format_base_url = None
        formats = []
        subtitles = {}
        # Live content
        if content_type == 20:
            dvr_enabled = traverse_obj(content_info, ('playerSettings', 'dvrEnabled'), expected_type=bool)
            format_base_url = f"https://{host}/live/{content_info['liveSourceID']}/{video_id}/%s{'?DVR' if dvr_enabled else ''}"
            live_status = 'is_live'
            heartbeat = self._call_api(host, f'heartbeat/{video_id}', video_id, note='Checking stream status') or {}
            if heartbeat.get('heartBeatUp') is False:
                self.raise_no_formats(f'MainStreaming said: {heartbeat.get("responseMessage")}', expected=True)
                live_status = 'was_live'

        # Playlist
        elif content_type == 31:
            return self.playlist_result(
                self._playlist_entries(host, content_info.get('playlistContents')), video_id, title, description)
        # Normal video content?
        elif content_type == 10:
            format_base_url = f'https://{host}/vod/{video_id}/%s'
            # Progressive format
            # Note: in https://webtools.msvdn.net/loader/playerV2.js there is mention of original.mp3 format,
            # however it seems to be the same as original.mp4?
            formats.append({'url': format_base_url % 'original.mp4', 'format_note': 'original', 'quality': 1})
        else:
            self.raise_no_formats(f'Unknown content type {content_type}')

        if format_base_url:
            m3u8_formats, m3u8_subs = self._extract_m3u8_formats_and_subtitles(
                format_base_url % 'playlist.m3u8', video_id=video_id, fatal=False)
            mpd_formats, mpd_subs = self._extract_mpd_formats_and_subtitles(
                format_base_url % 'manifest.mpd', video_id=video_id, fatal=False)

            subtitles = self._merge_subtitles(m3u8_subs, mpd_subs)
            formats.extend(m3u8_formats + mpd_formats)

        self._sort_formats(formats)

        return {
            'id': video_id,
            'title': title,
            'description': description,
            'formats': formats,
            'live_status': live_status,
            'duration': parse_duration(content_info.get('duration')),
            'tags': content_info.get('tags'),
            'subtitles': subtitles,
            'thumbnail': urljoin(self._get_webtools_base_url(host), f'image/{video_id}/poster')
        }


code:
from .common import InfoExtractor
from ..utils import (
    clean_html,
    dict_get,
    float_or_none,
    int_or_none,
    merge_dicts,
    parse_duration,
    try_get,
)

class MallTVIE(InfoExtractor):
    _VALID_URL = r'https?://(?:(?:www|sk)\.)?mall\.tv/(?:[^/]+/)*(?P<id>[^/?#&]+)'
    _TESTS = [{
        'url': 'https://www.mall.tv/18-miliard-pro-neziskovky-opravdu-jsou-sportovci-nebo-clovek-v-tisni-pijavice',
        'md5': '1c4a37f080e1f3023103a7b43458e518',
        'info_dict': {
            'id': 't0zzt0',
            'display_id': '18-miliard-pro-neziskovky-opravdu-jsou-sportovci-nebo-clovek-v-tisni-pijavice',
            'ext': 'mp4',
            'title': '18 miliard pro neziskovky. Opravdu jsou sportovci nebo Člověk v tísni pijavice?',
            'description': 'md5:db7d5744a4bd4043d9d98324aa72ab35',
            'duration': 216,
            'timestamp': 1538870400,
            'upload_date': '20181007',
            'view_count': int,
        }
    }, {
        'url': 'https://www.mall.tv/kdo-to-plati/18-miliard-pro-neziskovky-opravdu-jsou-sportovci-nebo-clovek-v-tisni-pijavice',
        'only_matching': True,
    }, {
        'url': 'https://sk.mall.tv/gejmhaus/reklamacia-nehreje-vyrobnik-tepla-alebo-spekacka',
        'only_matching': True,
    }]

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

        webpage = self._download_webpage(
            url, display_id, headers=self.geo_verification_headers())

        video = self._parse_json(self._search_regex(
            r'videoObject\s*=\s*JSON\.parse\(JSON\.stringify\(({.+?})\)\);',
            webpage, 'video object'), display_id)
        video_source = video['VideoSource']
        video_id = self._search_regex(
            r'/([\da-z]+)/index\b', video_source, 'video id')

        formats = self._extract_m3u8_formats(
            video_source + '.m3u8', video_id, 'mp4', 'm3u8_native')
        self._sort_formats(formats)

        subtitles = {}
        for s in (video.get('Subtitles') or {}):
            s_url = s.get('Url')
            if not s_url:
                continue
            subtitles.setdefault(s.get('Language') or 'cz', []).append({
                'url': s_url,
            })

        entity_counts = video.get('EntityCounts') or {}

        def get_count(k):
            v = entity_counts.get(k + 's') or {}
            return int_or_none(dict_get(v, ('Count', 'StrCount')))

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

        return merge_dicts({
            'id': video_id,
            'display_id': display_id,
            'title': video.get('Title'),
            'description': clean_html(video.get('Description')),
            'thumbnail': video.get('ThumbnailUrl'),
            'formats': formats,
            'subtitles': subtitles,
            'duration': int_or_none(video.get('DurationSeconds')) or parse_duration(video.get('Duration')),
            'view_count': get_count('View'),
            'like_count': get_count('Like'),
            'dislike_count': get_count('Dislike'),
            'average_rating': float_or_none(try_get(video, lambda x: x['EntityRating']['AvarageRate'])),
            'comment_count': get_count('Comment'),
        }, info)


code:
from .common import InfoExtractor
from ..compat import (
    compat_b64decode,
    compat_urllib_parse_unquote,
)
from ..utils import classproperty, int_or_none

class MangomoloBaseIE(InfoExtractor):
    _BASE_REGEX = r'(?:https?:)?//(?:admin\.mangomolo\.com/analytics/index\.php/customers/embed/|player\.mangomolo\.com/v1/)'
    _SLUG = None

    @classproperty
    def _VALID_URL(cls):
        return f'{cls._BASE_REGEX}{cls._SLUG}'

    @classproperty
    def _EMBED_REGEX(cls):
        return [rf'<iframe[^>]+src=(["\'])(?P<url>{cls._VALID_URL}.+?)\1']

    def _extract_from_webpage(self, url, webpage):
        for res in super()._extract_from_webpage(url, webpage):
            yield {
                **res,
                '_type': 'url_transparent',
                'id': self._search_regex(self._SLUG, res['url'], 'id', group='id'),
                'uploader': self._search_regex(r'^(?:https?://)?([^/]*)/.*', url, 'video uploader'),
            }

    def _get_real_id(self, page_id):
        return page_id

    def _real_extract(self, url):
        page_id = self._get_real_id(self._match_id(url))
        webpage = self._download_webpage(
            'https://player.mangomolo.com/v1/%s?%s' % (self._TYPE, url.split('?')[1]), page_id)
        hidden_inputs = self._hidden_inputs(webpage)
        m3u8_entry_protocol = 'm3u8' if self._IS_LIVE else 'm3u8_native'

        format_url = self._html_search_regex(
            [
                r'(?:file|src)\s*:\s*"(https?://[^"]+?/playlist\.m3u8)',
                r'<a[^>]+href="(rtsp://[^"]+)"'
            ], webpage, 'format url')
        formats = self._extract_wowza_formats(
            format_url, page_id, m3u8_entry_protocol, ['smil'])
        self._sort_formats(formats)

        return {
            'id': page_id,
            'title': page_id,
            'uploader_id': hidden_inputs.get('userid'),
            'duration': int_or_none(hidden_inputs.get('duration')),
            'is_live': self._IS_LIVE,
            'formats': formats,
        }

class MangomoloVideoIE(MangomoloBaseIE):
    _TYPE = 'video'
    IE_NAME = 'mangomolo:' + _TYPE
    _SLUG = r'video\?.*?\bid=(?P<id>\d+)'

    _IS_LIVE = False

class MangomoloLiveIE(MangomoloBaseIE):
    _TYPE = 'live'
    IE_NAME = 'mangomolo:' + _TYPE
    _SLUG = r'(?:live|index)\?.*?\bchannelid=(?P<id>(?:[A-Za-z0-9+/=]|%2B|%2F|%3D)+)'
    _IS_LIVE = True

    def _get_real_id(self, page_id):
        return compat_b64decode(compat_urllib_parse_unquote(page_id)).decode()


code:
from .common import InfoExtractor
from ..utils import (
    clean_html,
    int_or_none,
    traverse_obj
)

_API_URL = 'https://dak1vd5vmi7x6.cloudfront.net/api/v1/publicrole/{}/{}?id={}'

class ManotoTVIE(InfoExtractor):
    IE_DESC = 'Manoto TV (Episode)'
    _VALID_URL = r'https?://(?:www\.)?manototv\.com/episode/(?P<id>[0-9]+)'
    _TESTS = [{
        'url': 'https://www.manototv.com/episode/8475',
        'info_dict': {
            'id': '8475',
            'series': 'خانه های رویایی با برادران اسکات',
            'season_number': 7,
            'episode_number': 25,
            'episode_id': 'My Dream Home S7: Carol & John',
            'duration': 3600,
            'categories': ['سرگرمی'],
            'title': 'کارول و جان',
            'description': 'md5:d0fff1f8ba5c6775d312a00165d1a97e',
            'thumbnail': r're:^https?://.*\.(jpeg|png|jpg)$',
            'ext': 'mp4'
        },
        'params': {
            'skip_download': 'm3u8',
        }
    }, {
        'url': 'https://www.manototv.com/episode/12576',
        'info_dict': {
            'id': '12576',
            'series': 'فیلم های ایرانی',
            'episode_id': 'Seh Mah Taatili',
            'duration': 5400,
            'view_count': int,
            'categories': ['سرگرمی'],
            'title': 'سه ماه تعطیلی',
            'description': 'سه ماه تعطیلی فیلمی به کارگردانی و نویسندگی شاپور قریب ساختهٔ سال ۱۳۵۶ است.',
            'thumbnail': r're:^https?://.*\.(jpeg|png|jpg)$',
            'ext': 'mp4'
        },
        'params': {
            'skip_download': 'm3u8',
        }
    }]

    def _real_extract(self, url):
        video_id = self._match_id(url)
        episode_json = self._download_json(_API_URL.format('showmodule', 'episodedetails', video_id), video_id)
        details = episode_json.get('details', {})
        formats = self._extract_m3u8_formats(details.get('videoM3u8Url'), video_id, 'mp4')
        self._sort_formats(formats)
        return {
            'id': video_id,
            'series': details.get('showTitle'),
            'season_number': int_or_none(details.get('analyticsSeasonNumber')),
            'episode_number': int_or_none(details.get('episodeNumber')),
            'episode_id': details.get('analyticsEpisodeTitle'),
            'duration': int_or_none(details.get('durationInMinutes'), invscale=60),
            'view_count': details.get('viewCount'),
            'categories': [details.get('videoCategory')],
            'title': details.get('episodeTitle'),
            'description': clean_html(details.get('episodeDescription')),
            'thumbnail': details.get('episodelandscapeImgIxUrl'),
            'formats': formats,
        }

class ManotoTVShowIE(InfoExtractor):
    IE_DESC = 'Manoto TV (Show)'
    _VALID_URL = r'https?://(?:www\.)?manototv\.com/show/(?P<id>[0-9]+)'
    _TESTS = [{
        'url': 'https://www.manototv.com/show/2526',
        'playlist_mincount': 68,
        'info_dict': {
            'id': '2526',
            'title': 'فیلم های ایرانی',
            'description': 'مجموعه ای از فیلم های سینمای کلاسیک ایران',
        },
    }]

    def _real_extract(self, url):
        show_id = self._match_id(url)
        show_json = self._download_json(_API_URL.format('showmodule', 'details', show_id), show_id)
        show_details = show_json.get('details', {})
        title = show_details.get('showTitle')
        description = show_details.get('showSynopsis')

        series_json = self._download_json(_API_URL.format('showmodule', 'serieslist', show_id), show_id)
        playlist_id = str(traverse_obj(series_json, ('details', 'list', 0, 'id')))

        playlist_json = self._download_json(_API_URL.format('showmodule', 'episodelist', playlist_id), playlist_id)
        playlist = traverse_obj(playlist_json, ('details', 'list')) or []

        entries = [
            self.url_result(
                'https://www.manototv.com/episode/%s' % item['slideID'], ie=ManotoTVIE.ie_key(), video_id=item['slideID'])
            for item in playlist]
        return self.playlist_result(entries, show_id, title, description)

class ManotoTVLiveIE(InfoExtractor):
    IE_DESC = 'Manoto TV (Live)'
    _VALID_URL = r'https?://(?:www\.)?manototv\.com/live/'
    _TEST = {
        'url': 'https://www.manototv.com/live/',
        'info_dict': {
            'id': 'live',
            'title': 'Manoto TV Live',
            'ext': 'mp4',
            'is_live': True,
        },
        'params': {
            'skip_download': 'm3u8',
        }
    }

    def _real_extract(self, url):
        video_id = 'live'
        json = self._download_json(_API_URL.format('livemodule', 'details', ''), video_id)
        details = json.get('details', {})
        video_url = details.get('liveUrl')
        formats = self._extract_m3u8_formats(video_url, video_id, 'mp4', live=True)
        self._sort_formats(formats)
        return {
            'id': video_id,
            'title': 'Manoto TV Live',
            'is_live': True,
            'formats': formats,
        }


code:
from .common import InfoExtractor
from ..utils import (
    determine_ext,
    int_or_none,
    str_to_int,
    urlencode_postdata,
)

class ManyVidsIE(InfoExtractor):
    _VALID_URL = r'(?i)https?://(?:www\.)?manyvids\.com/video/(?P<id>\d+)'
    _TESTS = [{
        # preview video
        'url': 'https://www.manyvids.com/Video/133957/everthing-about-me/',
        'md5': '03f11bb21c52dd12a05be21a5c7dcc97',
        'info_dict': {
            'id': '133957',
            'ext': 'mp4',
            'title': 'everthing about me (Preview)',
            'view_count': int,
            'like_count': int,
        },
    }, {
        # full video
        'url': 'https://www.manyvids.com/Video/935718/MY-FACE-REVEAL/',
        'md5': 'f3e8f7086409e9b470e2643edb96bdcc',
        'info_dict': {
            'id': '935718',
            'ext': 'mp4',
            'title': 'MY FACE REVEAL',
            'view_count': int,
            'like_count': int,
        },
    }]

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

        webpage = self._download_webpage(url, video_id)

        video_url = self._search_regex(
            r'data-(?:video-filepath|meta-video)\s*=s*(["\'])(?P<url>(?:(?!\1).)+)\1',
            webpage, 'video URL', group='url')

        title = self._html_search_regex(
            (r'<span[^>]+class=["\']item-title[^>]+>([^<]+)',
             r'<h2[^>]+class=["\']h2 m-0["\'][^>]*>([^<]+)'),
            webpage, 'title', default=None) or self._html_search_meta(
            'twitter:title', webpage, 'title', fatal=True)

        if any(p in webpage for p in ('preview_videos', '_preview.mp4')):
            title += ' (Preview)'

        mv_token = self._search_regex(
            r'data-mvtoken=(["\'])(?P<value>(?:(?!\1).)+)\1', webpage,
            'mv token', default=None, group='value')

        if mv_token:
            # Sets some cookies
            self._download_webpage(
                'https://www.manyvids.com/includes/ajax_repository/you_had_me_at_hello.php',
                video_id, fatal=False, data=urlencode_postdata({
                    'mvtoken': mv_token,
                    'vid': video_id,
                }), headers={
                    'Referer': url,
                    'X-Requested-With': 'XMLHttpRequest'
                })

        if determine_ext(video_url) == 'm3u8':
            formats = self._extract_m3u8_formats(
                video_url, video_id, 'mp4', entry_protocol='m3u8_native',
                m3u8_id='hls')
        else:
            formats = [{'url': video_url}]

        like_count = int_or_none(self._search_regex(
            r'data-likes=["\'](\d+)', webpage, 'like count', default=None))
        view_count = str_to_int(self._html_search_regex(
            r'(?s)<span[^>]+class="views-wrapper"[^>]*>(.+?)</span', webpage,
            'view count', default=None))

        return {
            'id': video_id,
            'title': title,
            'view_count': view_count,
            'like_count': like_count,
            'formats': formats,
            'uploader': self._html_search_regex(r'<meta[^>]+name="author"[^>]*>([^<]+)', webpage, 'uploader'),
        }


code:
from .common import InfoExtractor

class MaoriTVIE(InfoExtractor):
    _VALID_URL = r'https?://(?:www\.)?maoritelevision\.com/shows/(?:[^/]+/)+(?P<id>[^/?&#]+)'
    _TEST = {
        'url': 'https://www.maoritelevision.com/shows/korero-mai/S01E054/korero-mai-series-1-episode-54',
        'md5': '5ade8ef53851b6a132c051b1cd858899',
        'info_dict': {
            'id': '4774724855001',
            'ext': 'mp4',
            'title': 'Kōrero Mai, Series 1 Episode 54',
            'upload_date': '20160226',
            'timestamp': 1456455018,
            'description': 'md5:59bde32fd066d637a1a55794c56d8dcb',
            'uploader_id': '1614493167001',
        },
    }
    BRIGHTCOVE_URL_TEMPLATE = 'http://players.brightcove.net/1614493167001/HJlhIQhQf_default/index.html?videoId=%s'

    def _real_extract(self, url):
        display_id = self._match_id(url)
        webpage = self._download_webpage(url, display_id)
        brightcove_id = self._search_regex(
            r'data-main-video-id=["\'](\d+)', webpage, 'brightcove id')
        return self.url_result(
            self.BRIGHTCOVE_URL_TEMPLATE % brightcove_id,
            'BrightcoveNew', brightcove_id)


code:
import re

from .common import InfoExtractor
from ..compat import compat_str
from ..utils import (
    orderedSet,
    parse_duration,
    try_get,
)

class MarkizaIE(InfoExtractor):
    _VALID_URL = r'https?://(?:www\.)?videoarchiv\.markiza\.sk/(?:video/(?:[^/]+/)*|embed/)(?P<id>\d+)(?:[_/]|$)'
    _TESTS = [{
        'url': 'http://videoarchiv.markiza.sk/video/oteckovia/84723_oteckovia-109',
        'md5': 'ada4e9fad038abeed971843aa028c7b0',
        'info_dict': {
            'id': '139078',
            'ext': 'mp4',
            'title': 'Oteckovia 109',
            'description': 'md5:d41d8cd98f00b204e9800998ecf8427e',
            'thumbnail': r're:^https?://.*\.jpg$',
            'duration': 2760,
        },
    }, {
        'url': 'http://videoarchiv.markiza.sk/video/televizne-noviny/televizne-noviny/85430_televizne-noviny',
        'info_dict': {
            'id': '85430',
            'title': 'Televízne noviny',
        },
        'playlist_count': 23,
    }, {
        'url': 'http://videoarchiv.markiza.sk/video/oteckovia/84723',
        'only_matching': True,
    }, {
        'url': 'http://videoarchiv.markiza.sk/video/84723',
        'only_matching': True,
    }, {
        'url': 'http://videoarchiv.markiza.sk/video/filmy/85190_kamenak',
        'only_matching': True,
    }, {
        'url': 'http://videoarchiv.markiza.sk/video/reflex/zo-zakulisia/84651_pribeh-alzbetky',
        'only_matching': True,
    }, {
        'url': 'http://videoarchiv.markiza.sk/embed/85295',
        'only_matching': True,
    }]

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

        data = self._download_json(
            'http://videoarchiv.markiza.sk/json/video_jwplayer7.json',
            video_id, query={'id': video_id})

        info = self._parse_jwplayer_data(data, m3u8_id='hls', mpd_id='dash')

        if info.get('_type') == 'playlist':
            info.update({
                'id': video_id,
                'title': try_get(
                    data, lambda x: x['details']['name'], compat_str),
            })
        else:
            info['duration'] = parse_duration(
                try_get(data, lambda x: x['details']['duration'], compat_str))
        return info

class MarkizaPageIE(InfoExtractor):
    _VALID_URL = r'https?://(?:www\.)?(?:(?:[^/]+\.)?markiza|tvnoviny)\.sk/(?:[^/]+/)*(?P<id>\d+)_'
    _TESTS = [{
        'url': 'http://www.markiza.sk/soubiz/zahranicny/1923705_oteckovia-maju-svoj-den-ti-slavni-nie-su-o-nic-menej-rozkosni',
        'md5': 'ada4e9fad038abeed971843aa028c7b0',
        'info_dict': {
            'id': '139355',
            'ext': 'mp4',
            'title': 'Oteckovia 110',
            'description': 'md5:d41d8cd98f00b204e9800998ecf8427e',
            'thumbnail': r're:^https?://.*\.jpg$',
            'duration': 2604,
        },
        'params': {
            'skip_download': True,
        },
    }, {
        'url': 'http://dajto.markiza.sk/filmy-a-serialy/1774695_frajeri-vo-vegas',
        'only_matching': True,
    }, {
        'url': 'http://superstar.markiza.sk/aktualne/1923870_to-je-ale-telo-spevacka-ukazala-sexy-postavicku-v-bikinach',
        'only_matching': True,
    }, {
        'url': 'http://hybsa.markiza.sk/aktualne/1923790_uzasna-atmosfera-na-hybsa-v-poprade-superstaristi-si-prve-koncerty-pred-davom-ludi-poriadne-uzili',
        'only_matching': True,
    }, {
        'url': 'http://doma.markiza.sk/filmy/1885250_moja-vysnivana-svadba',
        'only_matching': True,
    }, {
        'url': 'http://www.tvnoviny.sk/domace/1923887_po-smrti-manzela-ju-cakalo-poriadne-prekvapenie',
        'only_matching': True,
    }]

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

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

        webpage = self._download_webpage(
            # Downloading for some hosts (e.g. dajto, doma) fails with 500
            # although everything seems to be OK, so considering 500
            # status code to be expected.
            url, playlist_id, expected_status=500)

        entries = [
            self.url_result('http://videoarchiv.markiza.sk/video/%s' % video_id)
            for video_id in orderedSet(re.findall(
                r'(?:initPlayer_|data-entity=["\']|id=["\']player_)(\d+)',
                webpage))]

        return self.playlist_result(entries, playlist_id)


code:
import re

from .common import InfoExtractor
from ..utils import (
    clean_html,
    determine_ext,
    int_or_none,
    js_to_json,
    mimetype2ext,
    parse_filesize,
)

class MassengeschmackTVIE(InfoExtractor):
    IE_NAME = 'massengeschmack.tv'
    _VALID_URL = r'https?://(?:www\.)?massengeschmack\.tv/play/(?P<id>[^?&#]+)'

    _TEST = {
        'url': 'https://massengeschmack.tv/play/fktv202',
        'md5': 'a9e054db9c2b5a08f0a0527cc201e8d3',
        'info_dict': {
            'id': 'fktv202',
            'ext': 'mp4',
            'title': 'Fernsehkritik-TV - Folge 202',
        },
    }

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

        webpage = self._download_webpage(url, episode)
        title = clean_html(self._html_search_regex(
            '
([^<]+)
', webpage, 'title'))
        thumbnail = self._search_regex(r'POSTER\s*=\s*"([^"]+)', webpage, 'thumbnail', fatal=False)
        sources = self._parse_json(self._search_regex(r'(?s)MEDIA\s*=\s*(\[.+?\]);', webpage, 'media'), episode, js_to_json)

        formats = []
        for source in sources:
            furl = source.get('src')
            if not furl:
                continue
            furl = self._proto_relative_url(furl)
            ext = determine_ext(furl) or mimetype2ext(source.get('type'))
            if ext == 'm3u8':
                formats.extend(self._extract_m3u8_formats(
                    furl, episode, 'mp4', 'm3u8_native',
                    m3u8_id='hls', fatal=False))
            else:
                formats.append({
                    'url': furl,
                    'format_id': determine_ext(furl),
                })

        for (durl, format_id, width, height, filesize) in re.findall(r'''(?x)
                                   ]+?href="(?P<url>(?:https:)?//[^"]+)".*?
                                   (?P<format_id>.+?).*?
                                   (?:(?P<width>\d+)x(?P<height>\d+))?\s+?\((?P<filesize>[\d,]+\s*[GM]iB)\)
                                ''', webpage):
            formats.append({
                'url': durl,
                'format_id': format_id,
                'width': int_or_none(width),
                'height': int_or_none(height),
                'filesize': parse_filesize(filesize),
                'vcodec': 'none' if format_id.startswith('Audio') else None,
            })

        self._sort_formats(formats)

        return {
            'id': episode,
            'title': title,
            'formats': formats,
            'thumbnail': thumbnail,
        }


code:
from __future__ import unicode_literals
from .common import InfoExtractor
from ..utils import (
    traverse_obj,
    unified_strdate,
)

class MastersIE(InfoExtractor):
    _VALID_URL = r'https?://(?:www\.)?masters\.com/en_US/watch/(?P<date>\d{4}-\d{2}-\d{2})/(?P<id>\d+)'
    _TESTS = [{
        'url': 'https://www.masters.com/en_US/watch/2022-04-07/16493755593805191/sungjae_im_thursday_interview_2022.html',
        'info_dict': {
            'id': '16493755593805191',
            'ext': 'mp4',
            'title': 'Sungjae Im: Thursday Interview 2022',
            'upload_date': '20220407',
            'thumbnail': r're:^https?://.*\.jpg$',
        }
    }]

    def _real_extract(self, url):
        video_id, upload_date = self._match_valid_url(url).group('id', 'date')
        content_resp = self._download_json(
            f'https://www.masters.com/relatedcontent/rest/v2/masters_v1/en/content/masters_v1_{video_id}_en',
            video_id)
        formats, subtitles = self._extract_m3u8_formats_and_subtitles(traverse_obj(content_resp, ('media', 'm3u8')), video_id, 'mp4')
        self._sort_formats(formats)

        thumbnails = [{'id': name, 'url': url} for name, url in traverse_obj(content_resp, ('images', 0), default={}).items()]

        return {
            'id': video_id,
            'title': content_resp.get('title'),
            'formats': formats,
            'subtitles': subtitles,
            'upload_date': unified_strdate(upload_date),
            'thumbnails': thumbnails,
        }


code:
from __future__ import unicode_literals

from .mastodon import MastodonIE, MastodonUserIE, MastodonUserNumericIE

__all__ = ['MastodonIE', 'MastodonUserIE', 'MastodonUserNumericIE']