code:
# coding: utf-8
from __future__ import unicode_literals

import itertools
import json
import re

try:
    from .instances import instances
except ImportError:
    instances = ('gab.com', )

from ..common import SelfHostedInfoExtractor
from ..peertube.peertube import PeerTubeIE
from ...utils import (
    ExtractorError,
    clean_html,
    dict_get,
    make_archive_id,
    parse_duration,
    get_first_group,
    str_or_none,
    int_or_none,
    float_or_none,
    traverse_obj,
    try_get,
    parse_qs,
    url_or_none,
)
from ...compat import (
    compat_urllib_parse_urlencode as urlencode,
    compat_urlparse as urlparse,
)

known_valid_instances = set()

class MastodonBaseIE(SelfHostedInfoExtractor):
    _SH_VALID_CONTENT_STRINGS = (
        ',"settings":{"known_fediverse":',  # Mastodon initial-state
        '
Documentation
',
        '<title>Pleroma</title>',
        'To use Pleroma, please enable JavaScript.',
        'To use Soapbox, please enable JavaScript.',
        'Alternatively, try one of the native apps for Gab Social for your platform.',
    )
    _SH_VALID_CONTENT_SOFTWARES = ('mastodon', 'mastodon', 'pleroma', 'pleroma', 'pleroma', 'gab')
    _SH_VALID_CONTENT_REGEXES = (
        # double quotes on Mastodon, single quotes on Gab Social
        r'

repo_name: Lesmiscore/ytdl


code:
import random

from .common import InfoExtractor
from ..utils import xpath_text

class MatchTVIE(InfoExtractor):
    _VALID_URL = r'https?://matchtv\.ru(?:/on-air|/?#live-player)'
    _TESTS = [{
        'url': 'http://matchtv.ru/#live-player',
        'info_dict': {
            'id': 'matchtv-live',
            'ext': 'flv',
            'title': r're:^Матч ТВ - Прямой эфир \d{4}-\d{2}-\d{2} \d{2}:\d{2}$',
            'is_live': True,
        },
        'params': {
            'skip_download': True,
        },
    }, {
        'url': 'http://matchtv.ru/on-air/',
        'only_matching': True,
    }]

    def _real_extract(self, url):
        video_id = 'matchtv-live'
        video_url = self._download_json(
            'http://player.matchtv.ntvplus.tv/player/smil', video_id,
            query={
                'ts': '',
                'quality': 'SD',
                'contentId': '561d2c0df7159b37178b4567',
                'sign': '',
                'includeHighlights': '0',
                'userId': '',
                'sessionId': random.randint(1, 1000000000),
                'contentType': 'channel',
                'timeShift': '0',
                'platform': 'portal',
            },
            headers={
                'Referer': 'http://player.matchtv.ntvplus.tv/embed-player/NTVEmbedPlayer.swf',
            })['data']['videoUrl']
        f4m_url = xpath_text(self._download_xml(video_url, video_id), './to')
        formats = self._extract_f4m_formats(f4m_url, video_id)
        self._sort_formats(formats)
        return {
            'id': video_id,
            'title': 'Матч ТВ - Прямой эфир',
            'is_live': True,
            'formats': formats,
        }


code:
from .common import InfoExtractor
from ..compat import compat_urlparse
from ..utils import (
    determine_ext,
    int_or_none,
    join_nonempty,
    parse_duration,
    parse_iso8601,
    url_or_none,
    xpath_text,
)

class MDRIE(InfoExtractor):
    IE_DESC = 'MDR.DE and KiKA'
    _VALID_URL = r'https?://(?:www\.)?(?:mdr|kika)\.de/(?:.*)/[a-z-]+-?(?P<id>\d+)(?:_.+?)?\.html'

    _GEO_COUNTRIES = ['DE']

    _TESTS = [{
        # MDR regularly deletes its videos
        'url': 'http://www.mdr.de/fakt/video189002.html',
        'only_matching': True,
    }, {
        # audio
        'url': 'http://www.mdr.de/kultur/audio1312272_zc-15948bad_zs-86171fdd.html',
        'md5': '64c4ee50f0a791deb9479cd7bbe9d2fa',
        'info_dict': {
            'id': '1312272',
            'ext': 'mp3',
            'title': 'Feuilleton vom 30. Oktober 2015',
            'duration': 250,
            'uploader': 'MITTELDEUTSCHER RUNDFUNK',
        },
        'skip': '404 not found',
    }, {
        'url': 'http://www.kika.de/baumhaus/videos/video19636.html',
        'md5': '4930515e36b06c111213e80d1e4aad0e',
        'info_dict': {
            'id': '19636',
            'ext': 'mp4',
            'title': 'Baumhaus vom 30. Oktober 2015',
            'duration': 134,
            'uploader': 'KIKA',
        },
        'skip': '404 not found',
    }, {
        'url': 'http://www.kika.de/sendungen/einzelsendungen/weihnachtsprogramm/videos/video8182.html',
        'md5': '5fe9c4dd7d71e3b238f04b8fdd588357',
        'info_dict': {
            'id': '8182',
            'ext': 'mp4',
            'title': 'Beutolomäus und der geheime Weihnachtswunsch',
            'description': 'md5:b69d32d7b2c55cbe86945ab309d39bbd',
            'timestamp': 1482541200,
            'upload_date': '20161224',
            'duration': 4628,
            'uploader': 'KIKA',
        },
    }, {
        # audio with alternative playerURL pattern
        'url': 'http://www.mdr.de/kultur/videos-und-audios/audio-radio/operation-mindfuck-robert-wilson100.html',
        'info_dict': {
            'id': '100',
            'ext': 'mp4',
            'title': 'Feature: Operation Mindfuck - Robert Anton Wilson',
            'duration': 3239,
            'uploader': 'MITTELDEUTSCHER RUNDFUNK',
        },
    }, {
        # empty bitrateVideo and bitrateAudio
        'url': 'https://www.kika.de/filme/sendung128372_zc-572e3f45_zs-1d9fb70e.html',
        'info_dict': {
            'id': '128372',
            'ext': 'mp4',
            'title': 'Der kleine Wichtel kehrt zurück',
            'description': 'md5:f77fafdff90f7aa1e9dca14f662c052a',
            'duration': 4876,
            'timestamp': 1607823300,
            'upload_date': '20201213',
            'uploader': 'ZDF',
        },
        'params': {
            'skip_download': True,
        },
    }, {
        'url': 'http://www.kika.de/baumhaus/sendungen/video19636_zc-fea7f8a0_zs-4bf89c60.html',
        'only_matching': True,
    }, {
        'url': 'http://www.kika.de/sendungen/einzelsendungen/weihnachtsprogramm/einzelsendung2534.html',
        'only_matching': True,
    }, {
        'url': 'http://www.mdr.de/mediathek/mdr-videos/a/video-1334.html',
        'only_matching': True,
    }]

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

        webpage = self._download_webpage(url, video_id)

        data_url = self._search_regex(
            r'(?:dataURL|playerXml(?:["\'])?)\s*:\s*(["\'])(?P<url>.+?-avCustom\.xml)\1',
            webpage, 'data url', group='url').replace(r'\/', '/')

        doc = self._download_xml(
            compat_urlparse.urljoin(url, data_url), video_id)

        title = xpath_text(doc, ['./title', './broadcast/broadcastName'], 'title', fatal=True)

        type_ = xpath_text(doc, './type', default=None)

        formats = []
        processed_urls = []
        for asset in doc.findall('./assets/asset'):
            for source in (
                    'download',
                    'progressiveDownload',
                    'dynamicHttpStreamingRedirector',
                    'adaptiveHttpStreamingRedirector'):
                url_el = asset.find('./%sUrl' % source)
                if url_el is None:
                    continue

                video_url = url_or_none(url_el.text)
                if not video_url or video_url in processed_urls:
                    continue

                processed_urls.append(video_url)

                ext = determine_ext(video_url)
                if ext == 'm3u8':
                    formats.extend(self._extract_m3u8_formats(
                        video_url, video_id, 'mp4', entry_protocol='m3u8_native',
                        quality=1, m3u8_id='HLS', fatal=False))
                elif ext == 'f4m':
                    formats.extend(self._extract_f4m_formats(
                        video_url + '?hdcore=3.7.0&plugin=aasp-3.7.0.39.44', video_id,
                        quality=1, f4m_id='HDS', fatal=False))
                else:
                    media_type = xpath_text(asset, './mediaType', 'media type', default='MP4')
                    vbr = int_or_none(xpath_text(asset, './bitrateVideo', 'vbr'), 1000)
                    abr = int_or_none(xpath_text(asset, './bitrateAudio', 'abr'), 1000)
                    filesize = int_or_none(xpath_text(asset, './fileSize', 'file size'))

                    f = {
                        'url': video_url,
                        'format_id': join_nonempty(media_type, vbr or abr),
                        'filesize': filesize,
                        'abr': abr,
                        'vbr': vbr,
                    }

                    if vbr:
                        f.update({
                            'width': int_or_none(xpath_text(asset, './frameWidth', 'width')),
                            'height': int_or_none(xpath_text(asset, './frameHeight', 'height')),
                        })

                    if type_ == 'audio':
                        f['vcodec'] = 'none'

                    formats.append(f)

        self._sort_formats(formats)

        description = xpath_text(doc, './broadcast/broadcastDescription', 'description')
        timestamp = parse_iso8601(
            xpath_text(
                doc, [
                    './broadcast/broadcastDate',
                    './broadcast/broadcastStartDate',
                    './broadcast/broadcastEndDate'],
                'timestamp', default=None))
        duration = parse_duration(xpath_text(doc, './duration', 'duration'))
        uploader = xpath_text(doc, './rights', 'uploader')

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


code:
import re

from .common import InfoExtractor
from ..compat import compat_str
from ..utils import (
    ExtractorError,
    format_field,
    float_or_none,
    int_or_none,
    str_or_none,
    traverse_obj,
)

class MedalTVIE(InfoExtractor):
    _VALID_URL = r'https?://(?:www\.)?medal\.tv/(?P<path>games/[^/?#&]+/clips)/(?P<id>[^/?#&]+)'
    _TESTS = [{
        'url': 'https://medal.tv/games/valorant/clips/jTBFnLKdLy15K',
        'md5': '6930f8972914b6b9fdc2bb3918098ba0',
        'info_dict': {
            'id': 'jTBFnLKdLy15K',
            'ext': 'mp4',
            'title': "Mornu's clutch",
            'description': '',
            'uploader': 'Aciel',
            'timestamp': 1651628243,
            'upload_date': '20220504',
            'uploader_id': '19335460',
            'uploader_url': 'https://medal.tv/users/19335460',
            'comment_count': int,
            'view_count': int,
            'like_count': int,
            'duration': 13,
        }
    }, {
        'url': 'https://medal.tv/games/cod%20cold%20war/clips/2mA60jWAGQCBH',
        'md5': '3d19d426fe0b2d91c26e412684e66a06',
        'info_dict': {
            'id': '2mA60jWAGQCBH',
            'ext': 'mp4',
            'title': 'Quad Cold',
            'description': 'Medal,https://medal.tv/desktop/',
            'uploader': 'MowgliSB',
            'timestamp': 1603165266,
            'upload_date': '20201020',
            'uploader_id': '10619174',
            'thumbnail': 'https://cdn.medal.tv/10619174/thumbnail-34934644-720p.jpg?t=1080p&c=202042&missing',
            'uploader_url': 'https://medal.tv/users/10619174',
            'comment_count': int,
            'view_count': int,
            'like_count': int,
            'duration': 23,
        }
    }, {
        'url': 'https://medal.tv/games/cod%20cold%20war/clips/2um24TWdty0NA',
        'md5': 'b6dc76b78195fff0b4f8bf4a33ec2148',
        'info_dict': {
            'id': '2um24TWdty0NA',
            'ext': 'mp4',
            'title': 'u tk me i tk u bigger',
            'description': 'Medal,https://medal.tv/desktop/',
            'uploader': 'Mimicc',
            'timestamp': 1605580939,
            'upload_date': '20201117',
            'uploader_id': '5156321',
            'thumbnail': 'https://cdn.medal.tv/5156321/thumbnail-36787208-360p.jpg?t=1080p&c=202046&missing',
            'uploader_url': 'https://medal.tv/users/5156321',
            'comment_count': int,
            'view_count': int,
            'like_count': int,
            'duration': 9,
        }
    }, {
        'url': 'https://medal.tv/games/valorant/clips/37rMeFpryCC-9',
        'only_matching': True,
    }, {
        'url': 'https://medal.tv/games/valorant/clips/2WRj40tpY_EU9',
        'only_matching': True,
    }]

    def _real_extract(self, url):
        video_id = self._match_id(url)
        path = self._match_valid_url(url).group('path')

        webpage = self._download_webpage(url, video_id)

        next_data = self._search_json(
            '<script[^>]*__NEXT_DATA__[^>]*>', webpage,
            'next data', video_id, end_pattern='</script>', fatal=False)

        build_id = next_data.get('buildId')
        if not build_id:
            raise ExtractorError(
                'Could not find build ID.', video_id=video_id)

        locale = next_data.get('locale', 'en')

        api_response = self._download_json(
            f'https://medal.tv/_next/data/{build_id}/{locale}/{path}/{video_id}.json', video_id)

        clip = traverse_obj(api_response, ('pageProps', 'clip')) or {}
        if not clip:
            raise ExtractorError(
                'Could not find video information.', video_id=video_id)

        title = clip['contentTitle']

        source_width = int_or_none(clip.get('sourceWidth'))
        source_height = int_or_none(clip.get('sourceHeight'))

        aspect_ratio = source_width / source_height if source_width and source_height else 16 / 9

        def add_item(container, item_url, height, id_key='format_id', item_id=None):
            item_id = item_id or '%dp' % height
            if item_id not in item_url:
                return
            width = int(round(aspect_ratio * height))
            container.append({
                'url': item_url,
                id_key: item_id,
                'width': width,
                'height': height
            })

        formats = []
        thumbnails = []
        for k, v in clip.items():
            if not (v and isinstance(v, compat_str)):
                continue
            mobj = re.match(r'(contentUrl|thumbnail)(?:(\d+)p)?$', k)
            if not mobj:
                continue
            prefix = mobj.group(1)
            height = int_or_none(mobj.group(2))
            if prefix == 'contentUrl':
                add_item(
                    formats, v, height or source_height,
                    item_id=None if height else 'source')
            elif prefix == 'thumbnail':
                add_item(thumbnails, v, height, 'id')

        error = clip.get('error')
        if not formats and error:
            if error == 404:
                self.raise_no_formats(
                    'That clip does not exist.',
                    expected=True, video_id=video_id)
            else:
                self.raise_no_formats(
                    'An unknown error occurred ({0}).'.format(error),
                    video_id=video_id)

        self._sort_formats(formats)

        # Necessary because the id of the author is not known in advance.
        # Won't raise an issue if no profile can be found as this is optional.
        author = traverse_obj(api_response, ('pageProps', 'profile')) or {}
        author_id = str_or_none(author.get('userId'))
        author_url = format_field(author_id, None, 'https://medal.tv/users/%s')

        return {
            'id': video_id,
            'title': title,
            'formats': formats,
            'thumbnails': thumbnails,
            'description': clip.get('contentDescription'),
            'uploader': author.get('displayName'),
            'timestamp': float_or_none(clip.get('created'), 1000),
            'uploader_id': author_id,
            'uploader_url': author_url,
            'duration': int_or_none(clip.get('videoLengthSeconds')),
            'view_count': int_or_none(clip.get('views')),
            'like_count': int_or_none(clip.get('likes')),
            'comment_count': int_or_none(clip.get('comments')),
        }


code:
from .common import InfoExtractor

class MediaiteIE(InfoExtractor):
    _VALID_URL = r'https?://(?:www\.)?mediaite.com(?!/category)(?:/[\w-]+){2}'
    _TESTS = [{
        'url': 'https://www.mediaite.com/sports/bill-burr-roasts-nfl-for-promoting-black-lives-matter-while-scheduling-more-games-after-all-the-sht-they-know-about-cte/',
        'info_dict': {
            'id': 'vPHKITzy',
            'ext': 'm4a',
            'title': 'Bill Burr On NFL And Black Lives Matter',
            'description': 'md5:d41d8cd98f00b204e9800998ecf8427e',
            'thumbnail': 'https://cdn.jwplayer.com/v2/media/vPHKITzy/poster.jpg?width=720',
            'duration': 55,
            'timestamp': 1631630185,
            'upload_date': '20210914',
        },
        'params': {'skip_download': True}
    }, {
        'url': 'https://www.mediaite.com/tv/joe-scarborough-goes-off-on-tax-breaks-for-super-wealthy-largest-income-redistribution-scam-in-american-history/',
        'info_dict': {
            'id': 'eeFcK4Xm',
            'ext': 'mp4',
            'title': 'Morning Joe-6_16_52 am - 6_21_10 am-2021-09-14.mp4',
            'description': 'md5:d41d8cd98f00b204e9800998ecf8427e',
            'thumbnail': 'https://cdn.jwplayer.com/v2/media/eeFcK4Xm/poster.jpg?width=720',
            'duration': 258,
            'timestamp': 1631618057,
            'upload_date': '20210914',
        },
        'params': {'skip_download': True}
    }, {
        'url': 'https://www.mediaite.com/politics/watch-rudy-giuliani-impersonates-queen-elizabeth-calls-mark-milley-an-asshle-in-bizarre-9-11-speech/',
        'info_dict': {
            'id': 'EiyiXKcr',
            'ext': 'mp4',
            'title': 'Giuliani 1',
            'description': 'md5:d41d8cd98f00b204e9800998ecf8427e',
            'thumbnail': 'https://cdn.jwplayer.com/v2/media/EiyiXKcr/poster.jpg?width=720',
            'duration': 39,
            'timestamp': 1631536476,
            'upload_date': '20210913',
        },
        'params': {'skip_download': True}
    }, {
        'url': 'https://www.mediaite.com/podcasts/clarissa-ward-says-she-decided-to-become-a-journalist-on-9-11/',
        'info_dict': {
            'id': 'TxavoRTx',
            'ext': 'mp4',
            'title': 'clarissa-ward-3.mp4',
            'description': 'md5:d41d8cd98f00b204e9800998ecf8427e',
            'thumbnail': 'https://cdn.jwplayer.com/v2/media/TxavoRTx/poster.jpg?width=720',
            'duration': 83,
            'timestamp': 1631311188,
            'upload_date': '20210910',
        },
        'params': {'skip_download': True}
    }, {
        'url': 'https://www.mediaite.com/opinion/mainstream-media-ignores-rose-mcgowans-bombshell-allegation-that-newsoms-wife-tried-to-silence-her-on-weinstein/',
        'info_dict': {
            'id': 'sEIWvKR7',
            'ext': 'mp4',
            'title': 'KTTV_09-13-2021_05.34.21',
            'description': 'md5:d41d8cd98f00b204e9800998ecf8427e',
            'thumbnail': 'https://cdn.jwplayer.com/v2/media/sEIWvKR7/poster.jpg?width=720',
            'duration': 52,
            'timestamp': 1631553328,
            'upload_date': '20210913',
        },
        'params': {'skip_download': True}
    }, {
        'url': 'https://www.mediaite.com/news/watch-cnbcs-jim-cramer-says-nobody-wants-to-die-getting-infected-by-unvaccinated-coworker-even-for-22-an-hour/',
        'info_dict': {
            'id': 'nwpt1elX',
            'ext': 'mp4',
            'title': "CNBC's Jim Cramer Says Nobody Wants to Die Getting Infected by Unvaccinated Coworker 'Even for $22 an Hour'.mp4",
            'description': 'md5:d41d8cd98f00b204e9800998ecf8427e',
            'thumbnail': 'https://cdn.jwplayer.com/v2/media/nwpt1elX/poster.jpg?width=720',
            'duration': 60,
            'timestamp': 1633014214,
            'upload_date': '20210930',
        },
        'params': {'skip_download': True}
    }]

    def _real_extract(self, url):
        webpage = self._download_webpage(url, None)
        id = self._search_regex(r'data-video-id\s?=\s?\"([^\"]+)\"', webpage, 'id')
        data_json = self._download_json(f'https://cdn.jwplayer.com/v2/media/{id}', id)
        return self._parse_jwplayer_data(data_json)


code:
from ..utils import (
    unified_strdate
)
from .common import InfoExtractor
from ..compat import (
    compat_urllib_parse_unquote,
    compat_str
)

class MediaKlikkIE(InfoExtractor):
    _VALID_URL = r'''(?x)https?://(?:www\.)?
                        (?:mediaklikk|m4sport|hirado|petofilive)\.hu/.*?(?:videok?|cikk)/
                        (?:(?P<year>[0-9]{4})/(?P<month>[0-9]{1,2})/(?P<day>[0-9]{1,2})/)?
                        (?P<id>[^/#?_]+)'''

    _TESTS = [{
        # mediaklikk. date in html.
        'url': 'https://mediaklikk.hu/video/hazajaro-delnyugat-bacska-a-duna-menten-palankatol-doroszloig/',
        'info_dict': {
            'id': '4754129',
            'title': 'Hazajáró, DÉLNYUGAT-BÁCSKA – A Duna mentén Palánkától Doroszlóig',
            'ext': 'mp4',
            'upload_date': '20210901',
            'thumbnail': 'http://mediaklikk.hu/wp-content/uploads/sites/4/2014/02/hazajarouj_JO.jpg'
        }
    }, {
        # m4sport
        'url': 'https://m4sport.hu/video/2021/08/30/gyemant-liga-parizs/',
        'info_dict': {
            'id': '4754999',
            'title': 'Gyémánt Liga, Párizs',
            'ext': 'mp4',
            'upload_date': '20210830',
            'thumbnail': 'http://m4sport.hu/wp-content/uploads/sites/4/2021/08/vlcsnap-2021-08-30-18h21m20s10-1024x576.jpg'
        }
    }, {
        # m4sport with *video/ url and no date
        'url': 'https://m4sport.hu/bl-video/real-madrid-chelsea-1-1/',
        'info_dict': {
            'id': '4492099',
            'title': 'Real Madrid - Chelsea 1-1',
            'ext': 'mp4',
            'thumbnail': 'http://m4sport.hu/wp-content/uploads/sites/4/2021/04/Sequence-01.Still001-1024x576.png'
        }
    }, {
        # hirado
        'url': 'https://hirado.hu/videok/felteteleket-szabott-a-fovaros/',
        'info_dict': {
            'id': '4760120',
            'title': 'Feltételeket szabott a főváros',
            'ext': 'mp4',
            'thumbnail': 'http://hirado.hu/wp-content/uploads/sites/4/2021/09/vlcsnap-2021-09-01-20h20m37s165.jpg'
        }
    }, {
        # petofilive
        'url': 'https://petofilive.hu/video/2021/06/07/tha-shudras-az-akusztikban/',
        'info_dict': {
            'id': '4571948',
            'title': 'Tha Shudras az Akusztikban',
            'ext': 'mp4',
            'upload_date': '20210607',
            'thumbnail': 'http://petofilive.hu/wp-content/uploads/sites/4/2021/06/vlcsnap-2021-06-07-22h14m23s915-1024x576.jpg'
        }
    }]

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

        player_data_str = self._html_search_regex(
            r'mtva_player_manager\.player\(document.getElementById\(.*\),\s?(\{.*\}).*\);', webpage, 'player data')
        player_data = self._parse_json(player_data_str, display_id, compat_urllib_parse_unquote)
        video_id = compat_str(player_data['contentId'])
        title = player_data.get('title') or self._og_search_title(webpage, fatal=False) or \
            self._html_search_regex(r'<h\d+\b[^>]+\bclass="article_title">([^<]+)<', webpage, 'title')

        upload_date = unified_strdate(
            '%s-%s-%s' % (mobj.group('year'), mobj.group('month'), mobj.group('day')))
        if not upload_date:
            upload_date = unified_strdate(self._html_search_regex(
                r'<p+\b[^>]+\bclass="article_date">([^<]+)<', webpage, 'upload date', default=None))

        player_data['video'] = player_data.pop('token')
        player_page = self._download_webpage('https://player.mediaklikk.hu/playernew/player.php', video_id, query=player_data)
        playlist_url = self._proto_relative_url(compat_urllib_parse_unquote(
            self._html_search_regex(r'\"file\":\s*\"(\\?/\\?/.*playlist\.m3u8)\"', player_page, 'playlist_url')).replace('\\/', '/'))

        formats = self._extract_wowza_formats(
            playlist_url, video_id, skip_protocols=['f4m', 'smil', 'dash'])
        self._sort_formats(formats)

        return {
            'id': video_id,
            'title': title,
            'display_id': display_id,
            'formats': formats,
            'upload_date': upload_date,
            'thumbnail': player_data.get('bgImage') or self._og_search_thumbnail(webpage)
        }


code:
import re

from .common import InfoExtractor
from ..utils import (
    extract_attributes,
    int_or_none,
    mimetype2ext,
    parse_iso8601,
)

class MedialaanIE(InfoExtractor):
    _VALID_URL = r'''(?x)
                    https?://
                        (?:
                            (?:embed\.)?mychannels.video/embed/|
                            embed\.mychannels\.video/(?:s(?:dk|cript)/)?production/|
                            (?:www\.)?(?:
                                (?:
                                    7sur7|
                                    demorgen|
                                    hln|
                                    joe|
                                    qmusic
                                )\.be|
                                (?:
                                    [abe]d|
                                    bndestem|
                                    destentor|
                                    gelderlander|
                                    pzc|
                                    tubantia|
                                    volkskrant
                                )\.nl
                            )/video/(?:[^/]+/)*[^/?&#]+~p
                        )
                        (?P<id>\d+)
                    '''
    _TESTS = [{
        'url': 'https://www.bndestem.nl/video/de-terugkeer-van-ally-de-aap-en-wie-vertrekt-er-nog-bij-nac~p193993',
        'info_dict': {
            'id': '193993',
            'ext': 'mp4',
            'title': 'De terugkeer van Ally de Aap en wie vertrekt er nog bij NAC?',
            'timestamp': 1611663540,
            'upload_date': '20210126',
            'duration': 238,
        },
        'params': {
            'skip_download': True,
        },
    }, {
        'url': 'https://www.gelderlander.nl/video/kanalen/degelderlander~c320/series/snel-nieuws~s984/noodbevel-in-doetinchem-politie-stuurt-mensen-centrum-uit~p194093',
        'only_matching': True,
    }, {
        'url': 'https://embed.mychannels.video/sdk/production/193993?options=TFTFF_default',
        'only_matching': True,
    }, {
        'url': 'https://embed.mychannels.video/script/production/193993',
        'only_matching': True,
    }, {
        'url': 'https://embed.mychannels.video/production/193993',
        'only_matching': True,
    }, {
        'url': 'https://mychannels.video/embed/193993',
        'only_matching': True,
    }, {
        'url': 'https://embed.mychannels.video/embed/193993',
        'only_matching': True,
    }]

    @classmethod
    def _extract_embed_urls(cls, url, webpage):
        entries = []
        for element in re.findall(r'(<div[^>]+data-mychannels-type="video"[^>]*>)', webpage):
            mychannels_id = extract_attributes(element).get('data-mychannels-id')
            if mychannels_id:
                entries.append('https://mychannels.video/embed/' + mychannels_id)
        return entries

    def _real_extract(self, url):
        production_id = self._match_id(url)
        production = self._download_json(
            'https://embed.mychannels.video/sdk/production/' + production_id,
            production_id, query={'options': 'UUUU_default'})['productions'][0]
        title = production['title']

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

        return {
            'id': production_id,
            'title': title,
            'formats': formats,
            'thumbnail': production.get('posterUrl'),
            'timestamp': parse_iso8601(production.get('publicationDate'), ' '),
            'duration': int_or_none(production.get('duration')) or None,
        }


code:
import functools
import re

from .theplatform import ThePlatformBaseIE
from ..utils import (
    ExtractorError,
    GeoRestrictedError,
    int_or_none,
    OnDemandPagedList,
    parse_qs,
    try_get,
    urljoin,
    update_url_query,
)

class MediasetIE(ThePlatformBaseIE):
    _TP_TLD = 'eu'
    _VALID_URL = r'''(?x)
                    (?:
                        mediaset:|
                        https?://
                            (?:\w+\.)+mediaset\.it/
                            (?:
                                (?:video|on-demand|movie)/(?:[^/]+/)+[^/]+_|
                                player/(?:v\d+/)?index\.html\?.*?\bprogramGuid=
                            )
                    )(?P<id>[0-9A-Z]{16,})
                    '''
    _TESTS = [{
        # full episode
        'url': 'https://www.mediasetplay.mediaset.it/video/mrwronglezionidamore/episodio-1_F310575103000102',
        'md5': 'a7e75c6384871f322adb781d3bd72c26',
        'info_dict': {
            'id': 'F310575103000102',
            'ext': 'mp4',
            'title': 'Episodio 1',
            'description': 'md5:e8017b7d7194e9bfb75299c2b8d81e02',
            'thumbnail': r're:^https?://.*\.jpg$',
            'duration': 2682.0,
            'upload_date': '20210530',
            'series': 'Mr Wrong - Lezioni d\'amore',
            'timestamp': 1622413946,
            'uploader': 'Canale 5',
            'uploader_id': 'C5',
            'season': 'Season 1',
            'episode': 'Episode 1',
            'season_number': 1,
            'episode_number': 1,
            'chapters': [{'start_time': 0.0, 'end_time': 439.88}, {'start_time': 439.88, 'end_time': 1685.84}, {'start_time': 1685.84, 'end_time': 2682.0}],
        },
    }, {
        'url': 'https://www.mediasetplay.mediaset.it/video/matrix/puntata-del-25-maggio_F309013801000501',
        'md5': '1276f966ac423d16ba255ce867de073e',
        'info_dict': {
            'id': 'F309013801000501',
            'ext': 'mp4',
            'title': 'Puntata del 25 maggio',
            'description': 'md5:ee2e456e3eb1dba5e814596655bb5296',
            'thumbnail': r're:^https?://.*\.jpg$',
            'duration': 6565.008,
            'upload_date': '20200903',
            'series': 'Matrix',
            'timestamp': 1599172492,
            'uploader': 'Canale 5',
            'uploader_id': 'C5',
            'season': 'Season 5',
            'episode': 'Episode 5',
            'season_number': 5,
            'episode_number': 5,
            'chapters': [{'start_time': 0.0, 'end_time': 3409.08}, {'start_time': 3409.08, 'end_time': 6565.008}],
        },
    }, {
        'url': 'https://www.mediasetplay.mediaset.it/video/cameracafe5/episodio-69-pezzo-di-luna_F303843101017801',
        'md5': 'd1650ac9ff944f185556126a736df148',
        'info_dict': {
            'id': 'F303843101017801',
            'ext': 'mp4',
            'title': 'Episodio 69 - Pezzo di luna',
            'description': 'md5:7c32c8ec4118b72588b9412f11353f73',
            'thumbnail': r're:^https?://.*\.jpg$',
            'duration': 263.008,
            'upload_date': '20200902',
            'series': 'Camera Café 5',
            'timestamp': 1599064700,
            'uploader': 'Italia 1',
            'uploader_id': 'I1',
            'season': 'Season 5',
            'episode': 'Episode 178',
            'season_number': 5,
            'episode_number': 178,
            'chapters': [{'start_time': 0.0, 'end_time': 261.88}, {'start_time': 261.88, 'end_time': 263.008}],
        },
    }, {
        'url': 'https://www.mediasetplay.mediaset.it/video/cameracafe5/episodio-51-tu-chi-sei_F303843107000601',
        'md5': '567e9ad375b7a27a0e370650f572a1e3',
        'info_dict': {
            'id': 'F303843107000601',
            'ext': 'mp4',
            'title': 'Episodio 51 - Tu chi sei?',
            'description': 'md5:42ef006e56824cc31787a547590923f4',
            'thumbnail': r're:^https?://.*\.jpg$',
            'duration': 367.021,
            'upload_date': '20200902',
            'series': 'Camera Café 5',
            'timestamp': 1599069817,
            'uploader': 'Italia 1',
            'uploader_id': 'I1',
            'season': 'Season 5',
            'episode': 'Episode 6',
            'season_number': 5,
            'episode_number': 6,
            'chapters': [{'start_time': 0.0, 'end_time': 358.68}, {'start_time': 358.68, 'end_time': 367.021}],
        },
    }, {
        # movie
        'url': 'https://www.mediasetplay.mediaset.it/movie/selvaggi/selvaggi_F006474501000101',
        'md5': '720440187a2ae26af8148eb9e6b901ed',
        'info_dict': {
            'id': 'F006474501000101',
            'ext': 'mp4',
            'title': 'Selvaggi',
            'description': 'md5:cfdedbbfdd12d4d0e5dcf1fa1b75284f',
            'thumbnail': r're:^https?://.*\.jpg$',
            'duration': 5233.01,
            'upload_date': '20210729',
            'timestamp': 1627594716,
            'uploader': 'Cine34',
            'uploader_id': 'B6',
            'chapters': [{'start_time': 0.0, 'end_time': 1938.56}, {'start_time': 1938.56, 'end_time': 5233.01}],
        },
    }, {
        # clip
        'url': 'https://www.mediasetplay.mediaset.it/video/gogglebox/un-grande-classico-della-commedia-sexy_FAFU000000661680',
        'only_matching': True,
    }, {
        # iframe simple
        'url': 'https://static3.mediasetplay.mediaset.it/player/index.html?appKey=5ad3966b1de1c4000d5cec48&programGuid=FAFU000000665924&id=665924',
        'only_matching': True,
    }, {
        # iframe twitter (from http://www.wittytv.it/se-prima-mi-fidavo-zero/)
        'url': 'https://static3.mediasetplay.mediaset.it/player/index.html?appKey=5ad3966b1de1c4000d5cec48&programGuid=FAFU000000665104&id=665104',
        'only_matching': True,
    }, {
        # embedUrl (from https://www.wittytv.it/amici/est-ce-que-tu-maimes-gabriele-5-dicembre-copia/)
        'url': 'https://static3.mediasetplay.mediaset.it/player/v2/index.html?partnerId=wittytv&configId=&programGuid=FD00000000153323&autoplay=true&purl=http://www.wittytv.it/amici/est-ce-que-tu-maimes-gabriele-5-dicembre-copia/',
        'only_matching': True,
    }, {
        'url': 'mediaset:FAFU000000665924',
        'only_matching': True,
    }, {
        'url': 'https://www.mediasetplay.mediaset.it/video/mediasethaacuoreilfuturo/palmieri-alicudi-lisola-dei-tre-bambini-felici--un-decreto-per-alicudi-e-tutte-le-microscuole_FD00000000102295',
        'only_matching': True,
    }, {
        'url': 'https://www.mediasetplay.mediaset.it/video/cherryseason/anticipazioni-degli-episodi-del-23-ottobre_F306837101005C02',
        'only_matching': True,
    }, {
        'url': 'https://www.mediasetplay.mediaset.it/video/tg5/ambiente-onda-umana-per-salvare-il-pianeta_F309453601079D01',
        'only_matching': True,
    }, {
        'url': 'https://www.mediasetplay.mediaset.it/video/grandefratellovip/benedetta-una-doccia-gelata_F309344401044C135',
        'only_matching': True,
    }, {
        'url': 'https://www.mediasetplay.mediaset.it/movie/herculeslaleggendahainizio/hercules-la-leggenda-ha-inizio_F305927501000102',
        'only_matching': True,
    }, {
        'url': 'https://mediasetinfinity.mediaset.it/video/braveandbeautiful/episodio-113_F310948005000402',
        'only_matching': True,
    }, {
        'url': 'https://static3.mediasetplay.mediaset.it/player/v2/index.html?partnerId=wittytv&configId=&programGuid=FD00000000153323',
        'only_matching': True,
    }]

    def _extract_from_webpage(self, url, webpage):
        def _program_guid(qs):
            return qs.get('programGuid', [None])[0]

        for mobj in re.finditer(
                r'<iframe\b[^>]+\bsrc=(["\'])(?P<url>(?:https?:)?//(?:www\.)?video\.mediaset\.it/player/playerIFrame(?:Twitter)?\.shtml.*?)\1',
                webpage):
            embed_url = mobj.group('url')
            embed_qs = parse_qs(embed_url)
            program_guid = _program_guid(embed_qs)
            if program_guid:
                yield self.url_result(embed_url)
                continue

            video_id = embed_qs.get('id', [None])[0]
            if not video_id:
                continue
            urlh = self._request_webpage(embed_url, video_id, note='Following embed URL redirect')
            embed_url = urlh.geturl()
            program_guid = _program_guid(parse_qs(embed_url))
            if program_guid:
                yield self.url_result(embed_url)

    def _parse_smil_formats(self, smil, smil_url, video_id, namespace=None, f4m_params=None, transform_rtmp_url=None):
        for video in smil.findall(self._xpath_ns('.//video', namespace)):
            video.attrib['src'] = re.sub(r'(https?://vod05)t(-mediaset-it\.akamaized\.net/.+?.mpd)\?.+', r'\1\2', video.attrib['src'])
        return super(MediasetIE, self)._parse_smil_formats(smil, smil_url, video_id, namespace, f4m_params, transform_rtmp_url)

    def _check_drm_formats(self, tp_formats, video_id):
        has_nondrm, drm_manifest = False, ''
        for f in tp_formats:
            if '_sampleaes/' in (f.get('manifest_url') or ''):
                drm_manifest = drm_manifest or f['manifest_url']
                f['has_drm'] = True
            if not f.get('has_drm') and f.get('manifest_url'):
                has_nondrm = True

        nodrm_manifest = re.sub(r'_sampleaes/(\w+)_fp_', r'/\1_no_', drm_manifest)
        if has_nondrm or nodrm_manifest == drm_manifest:
            return

        tp_formats.extend(self._extract_m3u8_formats(
            nodrm_manifest, video_id, m3u8_id='hls', fatal=False) or [])

    def _real_extract(self, url):
        guid = self._match_id(url)
        tp_path = 'PR1GhC/media/guid/2702976343/' + guid
        info = self._extract_theplatform_metadata(tp_path, guid)

        formats = []
        subtitles = {}
        first_e = geo_e = None
        asset_type = 'geoNo:HD,browser,geoIT|geoNo:HD,geoIT|geoNo:SD,browser,geoIT|geoNo:SD,geoIT|geoNo|HD|SD'
        # TODO: fixup ISM+none manifest URLs
        for f in ('MPEG4', 'M3U'):
            try:
                tp_formats, tp_subtitles = self._extract_theplatform_smil(
                    update_url_query('http://link.theplatform.%s/s/%s' % (self._TP_TLD, tp_path), {
                        'mbr': 'true',
                        'formats': f,
                        'assetTypes': asset_type,
                    }), guid, 'Downloading %s SMIL data' % (f.split('+')[0]))
            except ExtractorError as e:
                if not geo_e and isinstance(e, GeoRestrictedError):
                    geo_e = e
                if not first_e:
                    first_e = e
                continue
            self._check_drm_formats(tp_formats, guid)
            formats.extend(tp_formats)
            subtitles = self._merge_subtitles(subtitles, tp_subtitles)

        # check for errors and report them
        if (first_e or geo_e) and not formats:
            raise geo_e or first_e

        self._sort_formats(formats)

        feed_data = self._download_json(
            'https://feed.entertainment.tv.theplatform.eu/f/PR1GhC/mediaset-prod-all-programs-v2/guid/-/' + guid,
            guid, fatal=False)
        if feed_data:
            publish_info = feed_data.get('mediasetprogram$publishInfo') or {}
            thumbnails = feed_data.get('thumbnails') or {}
            thumbnail = None
            for key, value in thumbnails.items():
                if key.startswith('image_keyframe_poster-'):
                    thumbnail = value.get('url')
                    break

            info.update({
                'description': info.get('description') or feed_data.get('description') or feed_data.get('longDescription'),
                'uploader': publish_info.get('description'),
                'uploader_id': publish_info.get('channel'),
                'view_count': int_or_none(feed_data.get('mediasetprogram$numberOfViews')),
                'thumbnail': thumbnail,
            })

            if feed_data.get('programType') == 'episode':
                info.update({
                    'episode_number': int_or_none(
                        feed_data.get('tvSeasonEpisodeNumber')),
                    'season_number': int_or_none(
                        feed_data.get('tvSeasonNumber')),
                    'series': feed_data.get('mediasetprogram$brandTitle'),
                })

        info.update({
            'id': guid,
            'formats': formats,
            'subtitles': subtitles,
        })
        return info

class MediasetShowIE(MediasetIE):
    _VALID_URL = r'''(?x)
                    (?:
                        https?://
                            (\w+\.)+mediaset\.it/
                            (?:
                                (?:fiction|programmi-tv|serie-tv|kids)/(?:.+?/)?
                                    (?:[a-z-]+)_SE(?P<id>\d{12})
                                    (?:,ST(?P<st>\d{12}))?
                                    (?:,sb(?P<sb>\d{9}))?$
                            )
                    )
                    '''
    _TESTS = [{
        # TV Show webpage (general webpage)
        'url': 'https://www.mediasetplay.mediaset.it/programmi-tv/leiene/leiene_SE000000000061',
        'info_dict': {
            'id': '000000000061',
            'title': 'Le Iene',
        },
        'playlist_mincount': 7,
    }, {
        # TV Show webpage (specific season)
        'url': 'https://www.mediasetplay.mediaset.it/programmi-tv/leiene/leiene_SE000000000061,ST000000002763',
        'info_dict': {
            'id': '000000002763',
            'title': 'Le Iene',
        },
        'playlist_mincount': 7,
    }, {
        # TV Show specific playlist (with multiple pages)
        'url': 'https://www.mediasetplay.mediaset.it/programmi-tv/leiene/iservizi_SE000000000061,ST000000002763,sb100013375',
        'info_dict': {
            'id': '100013375',
            'title': 'I servizi',
        },
        'playlist_mincount': 50,
    }]

    _BY_SUBBRAND = 'https://feed.entertainment.tv.theplatform.eu/f/PR1GhC/mediaset-prod-all-programs-v2?byCustomValue={subBrandId}{%s}&sort=:publishInfo_lastPublished|desc,tvSeasonEpisodeNumber|desc&range=%d-%d'
    _PAGE_SIZE = 25

    def _fetch_page(self, sb, page):
        lower_limit = page * self._PAGE_SIZE + 1
        upper_limit = lower_limit + self._PAGE_SIZE - 1
        content = self._download_json(
            self._BY_SUBBRAND % (sb, lower_limit, upper_limit), sb)
        for entry in content.get('entries') or []:
            yield self.url_result(
                'mediaset:' + entry['guid'],
                playlist_title=entry['mediasetprogram$subBrandDescription'])

    def _real_extract(self, url):
        playlist_id, st, sb = self._match_valid_url(url).group('id', 'st', 'sb')
        if not sb:
            page = self._download_webpage(url, st or playlist_id)
            entries = [self.url_result(urljoin('https://www.mediasetplay.mediaset.it', url))
                       for url in re.findall(r'href="([^<>=]+SE\d{12},ST\d{12},sb\d{9})">[^<]+<', page)]
            title = (self._html_search_regex(r'(?s)<h1[^>]*>(.+?)</h1>', page, 'title', default=None)
                     or self._og_search_title(page))
            return self.playlist_result(entries, st or playlist_id, title)

        entries = OnDemandPagedList(
            functools.partial(self._fetch_page, sb),
            self._PAGE_SIZE)
        title = try_get(entries, lambda x: x[0]['playlist_title'])

        return self.playlist_result(entries, sb, title)


code:
import re
import json

from .common import InfoExtractor
from ..compat import (
    compat_str,
    compat_urlparse,
)
from ..utils import (
    ExtractorError,
    float_or_none,
    mimetype2ext,
    str_or_none,
    try_call,
    try_get,
    smuggle_url,
    unsmuggle_url,
    url_or_none,
    urljoin,
)

_ID_RE = r'(?:[0-9a-f]{32,34}|[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12,14})'

class MediasiteIE(InfoExtractor):
    _VALID_URL = r'(?xi)https?://[^/]+/Mediasite/(?:Play|Showcase/[^/#?]+/Presentation)/(?P<id>%s)(?P<query>\?[^#]+|)' % _ID_RE
    _EMBED_REGEX = [r'(?xi)<iframe\b[^>]+\bsrc=(["\'])(?P<url>(?:(?:https?:)?//[^/]+)?/Mediasite/Play/%s(?:\?.*?)?)\1' % _ID_RE]
    _TESTS = [
        {
            'url': 'https://hitsmediaweb.h-its.org/mediasite/Play/2db6c271681e4f199af3c60d1f82869b1d',
            'info_dict': {
                'id': '2db6c271681e4f199af3c60d1f82869b1d',
                'ext': 'mp4',
                'title': 'Lecture: Tuesday, September 20, 2016 - Sir Andrew Wiles',
                'description': 'Sir Andrew Wiles: “Equations in arithmetic”\\n\\nI will describe some of the interactions between modern number theory and the problem of solving equations in rational numbers or integers\\u0027.',
                'timestamp': 1474268400.0,
                'upload_date': '20160919',
            },
        },
        {
            'url': 'http://mediasite.uib.no/Mediasite/Play/90bb363295d945d6b548c867d01181361d?catalog=a452b7df-9ae1-46b7-a3ba-aceeb285f3eb',
            'info_dict': {
                'id': '90bb363295d945d6b548c867d01181361d',
                'ext': 'mp4',
                'upload_date': '20150429',
                'title': '5) IT-forum 2015-Dag 1  - Dungbeetle -  How and why Rain created a tiny bug tracker for Unity',
                'timestamp': 1430311380.0,
            },
        },
        {
            'url': 'https://collegerama.tudelft.nl/Mediasite/Play/585a43626e544bdd97aeb71a0ec907a01d',
            'md5': '481fda1c11f67588c0d9d8fbdced4e39',
            'info_dict': {
                'id': '585a43626e544bdd97aeb71a0ec907a01d',
                'ext': 'mp4',
                'title': 'Een nieuwe wereld: waarden, bewustzijn en techniek van de mensheid 2.0.',
                'description': '',
                'thumbnail': r're:^https?://.*\.jpg(?:\?.*)?$',
                'duration': 7713.088,
                'timestamp': 1413309600,
                'upload_date': '20141014',
            },
        },
        {
            'url': 'https://collegerama.tudelft.nl/Mediasite/Play/86a9ea9f53e149079fbdb4202b521ed21d?catalog=fd32fd35-6c99-466c-89d4-cd3c431bc8a4',
            'md5': 'ef1fdded95bdf19b12c5999949419c92',
            'info_dict': {
                'id': '86a9ea9f53e149079fbdb4202b521ed21d',
                'ext': 'wmv',
                'title': '64ste Vakantiecursus: Afvalwater',
                'description': 'md5:7fd774865cc69d972f542b157c328305',
                'thumbnail': r're:^https?://.*\.jpg(?:\?.*?)?$',
                'duration': 10853,
                'timestamp': 1326446400,
                'upload_date': '20120113',
            },
        },
        {
            'url': 'http://digitalops.sandia.gov/Mediasite/Play/24aace4429fc450fb5b38cdbf424a66e1d',
            'md5': '9422edc9b9a60151727e4b6d8bef393d',
            'info_dict': {
                'id': '24aace4429fc450fb5b38cdbf424a66e1d',
                'ext': 'mp4',
                'title': 'Xyce Software Training - Section 1',
                'description': r're:(?s)SAND Number: SAND 2013-7800.{200,}',
                'upload_date': '20120409',
                'timestamp': 1333983600,
                'duration': 7794,
            }
        },
        {
            'url': 'https://collegerama.tudelft.nl/Mediasite/Showcase/livebroadcast/Presentation/ada7020854f743c49fbb45c9ec7dbb351d',
            'only_matching': True,
        },
        {
            'url': 'https://mediasite.ntnu.no/Mediasite/Showcase/default/Presentation/7d8b913259334b688986e970fae6fcb31d',
            'only_matching': True,
        },
        {
            # dashed id
            'url': 'https://hitsmediaweb.h-its.org/mediasite/Play/2db6c271-681e-4f19-9af3-c60d1f82869b1d',
            'only_matching': True,
        }
    ]

    # look in Mediasite.Core.js (Mediasite.ContentStreamType[*])
    _STREAM_TYPES = {
        0: 'video1',  # the main video
        2: 'slide',
        3: 'presentation',
        4: 'video2',  # screencast?
        5: 'video3',
    }

    @classmethod
    def _extract_embed_urls(cls, url, webpage):
        for embed_url in super()._extract_embed_urls(url, webpage):
            yield smuggle_url(embed_url, {'UrlReferrer': url})

    def __extract_slides(self, *, stream_id, snum, Stream, duration, images):
        slide_base_url = Stream['SlideBaseUrl']

        fname_template = Stream['SlideImageFileNameTemplate']
        if fname_template != 'slide_{0:D4}.jpg':
            self.report_warning('Unusual slide file name template; report a bug if slide downloading fails')
        fname_template = re.sub(r'\{0:D([0-9]+)\}', r'{0:0\1}', fname_template)

        fragments = []
        for i, slide in enumerate(Stream['Slides']):
            if i == 0:
                if slide['Time'] > 0:
                    default_slide = images.get('DefaultSlide')
                    if default_slide is None:
                        default_slide = images.get('DefaultStreamImage')
                    if default_slide is not None:
                        default_slide = default_slide['ImageFilename']
                    if default_slide is not None:
                        fragments.append({
                            'path': default_slide,
                            'duration': slide['Time'] / 1000,
                        })

            next_time = try_call(
                lambda: Stream['Slides'][i + 1]['Time'],
                lambda: duration,
                lambda: slide['Time'],
                expected_type=(int, float))

            fragments.append({
                'path': fname_template.format(slide.get('Number', i + 1)),
                'duration': (next_time - slide['Time']) / 1000
            })

        return {
            'format_id': '%s-%u.slides' % (stream_id, snum),
            'ext': 'mhtml',
            'url': slide_base_url,
            'protocol': 'mhtml',
            'acodec': 'none',
            'vcodec': 'none',
            'format_note': 'Slides',
            'fragments': fragments,
            'fragment_base_url': slide_base_url,
        }

    def _real_extract(self, url):
        url, data = unsmuggle_url(url, {})
        mobj = self._match_valid_url(url)
        resource_id = mobj.group('id')
        query = mobj.group('query')

        webpage, urlh = self._download_webpage_handle(url, resource_id)  # XXX: add UrlReferrer?
        redirect_url = urlh.geturl()

        # XXX: might have also extracted UrlReferrer and QueryString from the html
        service_path = compat_urlparse.urljoin(redirect_url, self._html_search_regex(
            r'<div[^>]+\bid=["\']ServicePath[^>]+>(.+?)</div>', webpage, resource_id,
            default='/Mediasite/PlayerService/PlayerService.svc/json'))

        player_options = self._download_json(
            '%s/GetPlayerOptions' % service_path, resource_id,
            headers={
                'Content-type': 'application/json; charset=utf-8',
                'X-Requested-With': 'XMLHttpRequest',
            },
            data=json.dumps({
                'getPlayerOptionsRequest': {
                    'ResourceId': resource_id,
                    'QueryString': query,
                    'UrlReferrer': data.get('UrlReferrer', ''),
                    'UseScreenReader': False,
                }
            }).encode('utf-8'))['d']

        presentation = player_options['Presentation']
        title = presentation['Title']

        if presentation is None:
            raise ExtractorError(
                'Mediasite says: %s' % player_options['PlayerPresentationStatusMessage'],
                expected=True)

        thumbnails = []
        formats = []
        for snum, Stream in enumerate(presentation['Streams']):
            stream_type = Stream.get('StreamType')
            if stream_type is None:
                continue

            video_urls = Stream.get('VideoUrls')
            if not isinstance(video_urls, list):
                video_urls = []

            stream_id = self._STREAM_TYPES.get(
                stream_type, 'type%u' % stream_type)

            stream_formats = []
            for unum, VideoUrl in enumerate(video_urls):
                video_url = url_or_none(VideoUrl.get('Location'))
                if not video_url:
                    continue
                # XXX: if Stream.get('CanChangeScheme', False), switch scheme to HTTP/HTTPS

                media_type = VideoUrl.get('MediaType')
                if media_type == 'SS':
                    stream_formats.extend(self._extract_ism_formats(
                        video_url, resource_id,
                        ism_id='%s-%u.%u' % (stream_id, snum, unum),
                        fatal=False))
                elif media_type == 'Dash':
                    stream_formats.extend(self._extract_mpd_formats(
                        video_url, resource_id,
                        mpd_id='%s-%u.%u' % (stream_id, snum, unum),
                        fatal=False))
                else:
                    stream_formats.append({
                        'format_id': '%s-%u.%u' % (stream_id, snum, unum),
                        'url': video_url,
                        'ext': mimetype2ext(VideoUrl.get('MimeType')),
                    })

            if Stream.get('HasSlideContent', False):
                images = player_options['PlayerLayoutOptions']['Images']
                stream_formats.append(self.__extract_slides(
                    stream_id=stream_id,
                    snum=snum,
                    Stream=Stream,
                    duration=presentation.get('Duration'),
                    images=images,
                ))

            # disprefer 'secondary' streams
            if stream_type != 0:
                for fmt in stream_formats:
                    fmt['quality'] = -10

            thumbnail_url = Stream.get('ThumbnailUrl')
            if thumbnail_url:
                thumbnails.append({
                    'id': '%s-%u' % (stream_id, snum),
                    'url': urljoin(redirect_url, thumbnail_url),
                    'preference': -1 if stream_type != 0 else 0,
                })
            formats.extend(stream_formats)

        self._sort_formats(formats)

        # XXX: Presentation['Presenters']
        # XXX: Presentation['Transcript']

        return {
            'id': resource_id,
            'title': title,
            'description': presentation.get('Description'),
            'duration': float_or_none(presentation.get('Duration'), 1000),
            'timestamp': float_or_none(presentation.get('UnixTime'), 1000),
            'formats': formats,
            'thumbnails': thumbnails,
        }

class MediasiteCatalogIE(InfoExtractor):
    _VALID_URL = r'''(?xi)
                        (?P<url>https?://[^/]+/Mediasite)
                        /Catalog/Full/
                        (?P<catalog_id>{0})
                        (?:
                            /(?P<current_folder_id>{0})
                            /(?P<root_dynamic_folder_id>{0})
                        )?
                    '''.format(_ID_RE)
    _TESTS = [{
        'url': 'http://events7.mediasite.com/Mediasite/Catalog/Full/631f9e48530d454381549f955d08c75e21',
        'info_dict': {
            'id': '631f9e48530d454381549f955d08c75e21',
            'title': 'WCET Summit: Adaptive Learning in Higher Ed: Improving Outcomes Dynamically',
        },
        'playlist_count': 6,
        'expected_warnings': ['is not a supported codec'],
    }, {
        # with CurrentFolderId and RootDynamicFolderId
        'url': 'https://medaudio.medicine.iu.edu/Mediasite/Catalog/Full/9518c4a6c5cf4993b21cbd53e828a92521/97a9db45f7ab47428c77cd2ed74bb98f14/9518c4a6c5cf4993b21cbd53e828a92521',
        'info_dict': {
            'id': '9518c4a6c5cf4993b21cbd53e828a92521',
            'title': 'IUSM Family and Friends Sessions',
        },
        'playlist_count': 2,
    }, {
        'url': 'http://uipsyc.mediasite.com/mediasite/Catalog/Full/d5d79287c75243c58c50fef50174ec1b21',
        'only_matching': True,
    }, {
        # no AntiForgeryToken
        'url': 'https://live.libraries.psu.edu/Mediasite/Catalog/Full/8376d4b24dd1457ea3bfe4cf9163feda21',
        'only_matching': True,
    }, {
        'url': 'https://medaudio.medicine.iu.edu/Mediasite/Catalog/Full/9518c4a6c5cf4993b21cbd53e828a92521/97a9db45f7ab47428c77cd2ed74bb98f14/9518c4a6c5cf4993b21cbd53e828a92521',
        'only_matching': True,
    }, {
        # dashed id
        'url': 'http://events7.mediasite.com/Mediasite/Catalog/Full/631f9e48-530d-4543-8154-9f955d08c75e',
        'only_matching': True,
    }]

    def _real_extract(self, url):
        mobj = self._match_valid_url(url)
        mediasite_url = mobj.group('url')
        catalog_id = mobj.group('catalog_id')
        current_folder_id = mobj.group('current_folder_id') or catalog_id
        root_dynamic_folder_id = mobj.group('root_dynamic_folder_id')

        webpage = self._download_webpage(url, catalog_id)

        # AntiForgeryToken is optional (e.g. [1])
        # 1. https://live.libraries.psu.edu/Mediasite/Catalog/Full/8376d4b24dd1457ea3bfe4cf9163feda21
        anti_forgery_token = self._search_regex(
            r'AntiForgeryToken\s*:\s*(["\'])(?P<value>(?:(?!\1).)+)\1',
            webpage, 'anti forgery token', default=None, group='value')
        if anti_forgery_token:
            anti_forgery_header = self._search_regex(
                r'AntiForgeryHeaderName\s*:\s*(["\'])(?P<value>(?:(?!\1).)+)\1',
                webpage, 'anti forgery header name',
                default='X-SOFO-AntiForgeryHeader', group='value')

        data = {
            'IsViewPage': True,
            'IsNewFolder': True,
            'AuthTicket': None,
            'CatalogId': catalog_id,
            'CurrentFolderId': current_folder_id,
            'RootDynamicFolderId': root_dynamic_folder_id,
            'ItemsPerPage': 1000,
            'PageIndex': 0,
            'PermissionMask': 'Execute',
            'CatalogSearchType': 'SearchInFolder',
            'SortBy': 'Date',
            'SortDirection': 'Descending',
            'StartDate': None,
            'EndDate': None,
            'StatusFilterList': None,
            'PreviewKey': None,
            'Tags': [],
        }

        headers = {
            'Content-Type': 'application/json; charset=UTF-8',
            'Referer': url,
            'X-Requested-With': 'XMLHttpRequest',
        }
        if anti_forgery_token:
            headers[anti_forgery_header] = anti_forgery_token

        catalog = self._download_json(
            '%s/Catalog/Data/GetPresentationsForFolder' % mediasite_url,
            catalog_id, data=json.dumps(data).encode(), headers=headers)

        entries = []
        for video in catalog['PresentationDetailsList']:
            if not isinstance(video, dict):
                continue
            video_id = str_or_none(video.get('Id'))
            if not video_id:
                continue
            entries.append(self.url_result(
                '%s/Play/%s' % (mediasite_url, video_id),
                ie=MediasiteIE.ie_key(), video_id=video_id))

        title = try_get(
            catalog, lambda x: x['CurrentFolder']['Name'], compat_str)

        return self.playlist_result(entries, catalog_id, title,)

class MediasiteNamedCatalogIE(InfoExtractor):
    _VALID_URL = r'(?xi)(?P<url>https?://[^/]+/Mediasite)/Catalog/catalogs/(?P<catalog_name>[^/?#&]+)'
    _TESTS = [{
        'url': 'https://msite.misis.ru/Mediasite/Catalog/catalogs/2016-industrial-management-skriabin-o-o',
        'only_matching': True,
    }]

    def _real_extract(self, url):
        mobj = self._match_valid_url(url)
        mediasite_url = mobj.group('url')
        catalog_name = mobj.group('catalog_name')

        webpage = self._download_webpage(url, catalog_name)

        catalog_id = self._search_regex(
            r'CatalogId\s*:\s*["\'](%s)' % _ID_RE, webpage, 'catalog id')

        return self.url_result(
            '%s/Catalog/Full/%s' % (mediasite_url, catalog_id),
            ie=MediasiteCatalogIE.ie_key(), video_id=catalog_id)


code:
import re

from .common import InfoExtractor
from ..utils import (
    bug_reports_message,
    float_or_none,
    traverse_obj,
    unified_timestamp,
)

class MediaWorksNZVODIE(InfoExtractor):
    _VALID_URL_BASE_RE = r'https?://vodupload-api\.mediaworks\.nz/library/asset/published/'
    _VALID_URL_ID_RE = r'(?P<id>[A-Za-z0-9-]+)'
    _VALID_URL = rf'{_VALID_URL_BASE_RE}{_VALID_URL_ID_RE}'
    _TESTS = [{
        'url': 'https://vodupload-api.mediaworks.nz/library/asset/published/VID00359',
        'info_dict': {
            'id': 'VID00359',
            'ext': 'mp4',
            'title': 'GRG Jacinda Ardern safe drug testing 1920x1080',
            'description': 'md5:d4d7dc366742e86d8130b257dcb520ba',
            'duration': 142.76,
            'timestamp': 1604268608,
            'upload_date': '20201101',
            'thumbnail': r're:^https?://.*\.jpg$',
            'channel': 'George FM'
        }
    }, {
        # has audio-only format
        'url': 'https://vodupload-api.mediaworks.nz/library/asset/published/VID02627',
        'info_dict': {
            'id': 'VID02627',
            'ext': 'mp3',
            'title': 'Tova O\'Brien meets Ukraine President Volodymyr Zelensky',
            'channel': 'Today FM',
            'description': 'Watch in full the much anticipated interview of Volodymyr Zelensky',
            'duration': 2061.16,
            'thumbnail': r're:^https?://.*\.jpg$',
            'upload_date': '20220822',
            'timestamp': 1661152289,
        },
        'params': {'format': 'ba[ext=mp3]'}
    }]

    _WEBPAGE_TESTS = [{
        'url': 'https://www.rova.nz/home/podcasts/socrates-walks-into-a-bar/the-trolley-problem---episode-1.html',
        'info_dict': {
            'id': 'VID02494',
            'ext': 'mp4',
            'title': 'The Trolley Problem',
            'duration': 2843.56,
            'channel': 'Other',
            'timestamp': 1658356489,
            'thumbnail': r're:^https?://.*\.jpg$',
            'description': 'Socrates Walks Into A Bar Podcast Episode 1',
            'upload_date': '20220720',
        }
    }]

    @classmethod
    def _extract_embed_urls(cls, url, webpage):
        for mobj in re.finditer(
            rf'''(?x)<div\s+\bid=["']Player-Attributes-JWID[^>]+\b
            data-request-url=["']{cls._VALID_URL_BASE_RE}["'][^>]+\b
            data-asset-id=["']{cls._VALID_URL_ID_RE}["']''', webpage
        ):
            yield f'https://vodupload-api.mediaworks.nz/library/asset/published/{mobj.group("id")}'

    def _real_extract(self, url):
        video_id = self._match_id(url)
        asset = self._download_json(url, video_id)['asset']

        if asset.get('drm') not in ('NonDRM', None):
            self.report_drm(video_id)

        content_type = asset.get('type')
        if content_type and content_type != 'video':
            self.report_warning(f'Unknown content type: {content_type}' + bug_reports_message(), video_id)

        formats, subtitles = self._extract_m3u8_formats_and_subtitles(asset['streamingUrl'], video_id)

        audio_streaming_url = traverse_obj(
            asset, 'palyoutPathAudio', 'playoutpathaudio', expected_type=str)
        if audio_streaming_url:
            audio_formats = self._extract_m3u8_formats(audio_streaming_url, video_id, fatal=False, ext='mp3')
            for audio_format in audio_formats:
                # all the audio streams appear to be aac
                audio_format.setdefault('vcodec', 'none')
                audio_format.setdefault('acodec', 'aac')
                formats.append(audio_format)

        self._sort_formats(formats)

        return {
            'id': video_id,
            'title': asset.get('title'),
            'description': asset.get('description'),
            'duration': float_or_none(asset.get('duration')),
            'timestamp': unified_timestamp(asset.get('dateadded')),
            'channel': asset.get('brand'),
            'thumbnails': [{'url': thumbnail_url} for thumbnail_url in asset.get('thumbnails') or []],
            'formats': formats,
            'subtitles': subtitles,
        }


code:
from .common import InfoExtractor
from ..utils import (
    unified_strdate,
    update_url_query,
    urlencode_postdata,
)

class MediciIE(InfoExtractor):
    _VALID_URL = r'https?://(?:www\.)?medici\.tv/#!/(?P<id>[^?#&]+)'
    _TEST = {
        'url': 'http://www.medici.tv/#!/daniel-harding-frans-helmerson-verbier-festival-music-camp',
        'md5': '004c21bb0a57248085b6ff3fec72719d',
        'info_dict': {
            'id': '3059',
            'ext': 'flv',
            'title': 'Daniel Harding conducts the Verbier Festival Music Camp \u2013 With Frans Helmerson',
            'description': 'md5:322a1e952bafb725174fd8c1a8212f58',
            'thumbnail': r're:^https?://.*\.jpg$',
            'upload_date': '20170408',
        },
    }

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

        # Sets csrftoken cookie
        self._download_webpage(url, video_id)

        MEDICI_URL = 'http://www.medici.tv/'

        data = self._download_json(
            MEDICI_URL, video_id,
            data=urlencode_postdata({
                'json': 'true',
                'page': '/%s' % video_id,
                'timezone_offset': -420,
            }), headers={
                'X-CSRFToken': self._get_cookies(url)['csrftoken'].value,
                'X-Requested-With': 'XMLHttpRequest',
                'Referer': MEDICI_URL,
                'Content-Type': 'application/x-www-form-urlencoded',
            })

        video = data['video']['videos']['video1']

        title = video.get('nom') or data['title']

        video_id = video.get('id') or video_id
        formats = self._extract_f4m_formats(
            update_url_query(video['url_akamai'], {
                'hdcore': '3.1.0',
                'plugin=aasp': '3.1.0.43.124',
            }), video_id, f4m_id='hds')

        description = data.get('meta_description')
        thumbnail = video.get('url_thumbnail') or data.get('main_image')
        upload_date = unified_strdate(data['video'].get('date'))

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


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

class MegaphoneIE(InfoExtractor):
    IE_NAME = 'megaphone.fm'
    IE_DESC = 'megaphone.fm embedded players'
    _VALID_URL = r'https://player\.megaphone\.fm/(?P<id>[A-Z0-9]+)'
    _EMBED_REGEX = [rf'<iframe[^>]*?\ssrc=["\'](?P<url>{_VALID_URL})']
    _TEST = {
        'url': 'https://player.megaphone.fm/GLT9749789991?"',
        'md5': '4816a0de523eb3e972dc0dda2c191f96',
        'info_dict': {
            'id': 'GLT9749789991',
            'ext': 'mp3',
            'title': '#97 What Kind Of Idiot Gets Phished?',
            'thumbnail': r're:^https://.*\.png.*$',
            'duration': 1776.26375,
            'author': 'Reply All',
        },
    }

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

        title = self._og_search_property('audio:title', webpage)
        author = self._og_search_property('audio:artist', webpage)
        thumbnail = self._og_search_thumbnail(webpage)

        episode_json = self._search_regex(r'(?s)var\s+episode\s*=\s*(\{.+?\});', webpage, 'episode JSON')
        episode_data = self._parse_json(episode_json, video_id, js_to_json)
        video_url = self._proto_relative_url(episode_data['mediaUrl'], 'https:')

        formats = [{
            'url': video_url,
        }]

        return {
            'id': video_id,
            'thumbnail': thumbnail,
            'title': title,
            'author': author,
            'duration': episode_data['duration'],
            'formats': formats,
        }


code:
import re

from .common import InfoExtractor
from ..utils import (
    clean_html,
    determine_ext,
    ExtractorError,
    extract_attributes,
    get_element_by_class,
    get_element_html_by_id,
    HEADRequest,
    parse_qs,
    unescapeHTML,
    unified_timestamp,
)

class MegaTVComBaseIE(InfoExtractor):
    _PLAYER_DIV_ID = 'player_div_id'

    def _extract_player_attrs(self, webpage):
        player_el = get_element_html_by_id(self._PLAYER_DIV_ID, webpage)
        return {
            re.sub(r'^data-(?:kwik_)?', '', k): v
            for k, v in extract_attributes(player_el).items()
            if k not in ('id',)
        }

class MegaTVComIE(MegaTVComBaseIE):
    IE_NAME = 'megatvcom'
    IE_DESC = 'megatv.com videos'
    _VALID_URL = r'https?://(?:www\.)?megatv\.com/(?:\d{4}/\d{2}/\d{2}|[^/]+/(?P<id>\d+))/(?P<slug>[^/]+)'

    _TESTS = [{
        'url': 'https://www.megatv.com/2021/10/23/egkainia-gia-ti-nea-skini-omega-tou-dimotikou-theatrou-peiraia/',
        'md5': '6546a1a37fff0dd51c9dce5f490b7d7d',
        'info_dict': {
            'id': '520979',
            'ext': 'mp4',
            'title': 'md5:70eef71a9cd2c1ecff7ee428354dded2',
            'description': 'md5:0209fa8d318128569c0d256a5c404db1',
            'timestamp': 1634975747,
            'upload_date': '20211023',
            'display_id': 'egkainia-gia-ti-nea-skini-omega-tou-dimotikou-theatrou-peiraia',
            'thumbnail': 'https://www.megatv.com/wp-content/uploads/2021/10/ΠΕΙΡΑΙΑΣ-1024x450.jpg',
        },
    }, {
        'url': 'https://www.megatv.com/tvshows/527800/epeisodio-65-12/',
        'md5': 'cba2085d45c1abeb8e7e9b7e1d6c0072',
        'info_dict': {
            'id': '527800',
            'ext': 'mp4',
            'title': 'md5:fc322cb51f682eecfe2f54cd5ab3a157',
            'description': 'md5:b2b7ed3690a78f2a0156eb790fdc00df',
            'timestamp': 1636048859,
            'upload_date': '20211104',
            'display_id': 'epeisodio-65-12',
            'thumbnail': 'https://www.megatv.com/wp-content/uploads/2021/11/16-1-1.jpg',
        },
    }]

    def _real_extract(self, url):
        video_id, display_id = self._match_valid_url(url).group('id', 'slug')
        _is_article = video_id is None
        webpage = self._download_webpage(url, video_id or display_id)
        if _is_article:
            video_id = self._search_regex(
                r'<article[^>]*\sid=["\']Article_(\d+)["\']', webpage, 'article id')
        player_attrs = self._extract_player_attrs(webpage)
        title = player_attrs.get('label') or self._og_search_title(webpage)
        description = get_element_by_class(
            'article-wrapper' if _is_article else 'story_content',
            webpage)
        description = clean_html(re.sub(r'<script[^>]*>[^<]+</script>', '', description))
        if not description:
            description = self._og_search_description(webpage)
        thumbnail = player_attrs.get('image') or self._og_search_thumbnail(webpage)
        timestamp = unified_timestamp(self._html_search_meta(
            'article:published_time', webpage))
        source = player_attrs.get('source')
        if not source:
            raise ExtractorError('No source found', video_id=video_id)
        if determine_ext(source) == 'm3u8':
            formats, subs = self._extract_m3u8_formats_and_subtitles(source, video_id, 'mp4')
        else:
            formats, subs = [{'url': source}], {}
        if player_attrs.get('subs'):
            self._merge_subtitles({'und': [{'url': player_attrs['subs']}]}, target=subs)
        self._sort_formats(formats)
        return {
            'id': video_id,
            'display_id': display_id,
            'title': title,
            'description': description,
            'thumbnail': thumbnail,
            'timestamp': timestamp,
            'formats': formats,
            'subtitles': subs,
        }

class MegaTVComEmbedIE(MegaTVComBaseIE):
    IE_NAME = 'megatvcom:embed'
    IE_DESC = 'megatv.com embedded videos'
    _VALID_URL = r'(?:https?:)?//(?:www\.)?megatv\.com/embed/?\?p=(?P<id>\d+)'
    _EMBED_REGEX = [rf'''<iframe[^>]+?src=(?P<_q1>["'])(?P<url>{_VALID_URL})(?P=_q1)''']

    _TESTS = [{
        'url': 'https://www.megatv.com/embed/?p=2020520979',
        'md5': '6546a1a37fff0dd51c9dce5f490b7d7d',
        'info_dict': {
            'id': '520979',
            'ext': 'mp4',
            'title': 'md5:70eef71a9cd2c1ecff7ee428354dded2',
            'description': 'md5:0209fa8d318128569c0d256a5c404db1',
            'timestamp': 1634975747,
            'upload_date': '20211023',
            'display_id': 'egkainia-gia-ti-nea-skini-omega-tou-dimotikou-theatrou-peiraia',
            'thumbnail': 'https://www.megatv.com/wp-content/uploads/2021/10/ΠΕΙΡΑΙΑΣ-1024x450.jpg',
        },
    }, {
        'url': 'https://www.megatv.com/embed/?p=2020534081',
        'md5': '6ac8b3ce4dc6120c802f780a1e6b3812',
        'info_dict': {
            'id': '534081',
            'ext': 'mp4',
            'title': 'md5:062e9d5976ef854d8bdc1f5724d9b2d0',
            'description': 'md5:36dbe4c3762d2ede9513eea8d07f6d52',
            'timestamp': 1636376351,
            'upload_date': '20211108',
            'display_id': 'neo-rekor-stin-timi-tou-ilektrikou-reymatos-pano-apo-ta-200e-i-xondriki-timi-tou-ilektrikou',
            'thumbnail': 'https://www.megatv.com/wp-content/uploads/2021/11/Capture-266.jpg',
        },
    }]

    def _match_canonical_url(self, webpage):
        LINK_RE = r'''(?x)
        <link(?:
            rel=(?P<_q1>["'])(?P<canonical>canonical)(?P=_q1)|
            href=(?P<_q2>["'])(?P<href>(?:(?!(?P=_q2)).)+)(?P=_q2)|
            [^>]*?
        )+>
        '''
        for mobj in re.finditer(LINK_RE, webpage):
            canonical, href = mobj.group('canonical', 'href')
            if canonical and href:
                return unescapeHTML(href)

    def _real_extract(self, url):
        video_id = self._match_id(url)
        webpage = self._download_webpage(url, video_id)
        player_attrs = self._extract_player_attrs(webpage)
        canonical_url = player_attrs.get('share_url') or self._match_canonical_url(webpage)
        if not canonical_url:
            raise ExtractorError('canonical URL not found')
        video_id = parse_qs(canonical_url)['p'][0]

        # Defer to megatvcom as the metadata extracted from the embeddable page some
        # times are slightly different, for the same video
        canonical_url = self._request_webpage(
            HEADRequest(canonical_url), video_id,
            note='Resolve canonical URL',
            errnote='Could not resolve canonical URL').geturl()
        return self.url_result(canonical_url, MegaTVComIE.ie_key(), video_id)


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

class MeipaiIE(InfoExtractor):
    IE_DESC = '美拍'
    _VALID_URL = r'https?://(?:www\.)?meipai\.com/media/(?P<id>[0-9]+)'
    _TESTS = [{
        # regular uploaded video
        'url': 'http://www.meipai.com/media/531697625',
        'md5': 'e3e9600f9e55a302daecc90825854b4f',
        'info_dict': {
            'id': '531697625',
            'ext': 'mp4',
            'title': '#葉子##阿桑##余姿昀##超級女聲#',
            'description': '#葉子##阿桑##余姿昀##超級女聲#',
            'thumbnail': r're:^https?://.*\.jpg$',
            'duration': 152,
            'timestamp': 1465492420,
            'upload_date': '20160609',
            'view_count': 35511,
            'creator': '她她-TATA',
            'tags': ['葉子', '阿桑', '余姿昀', '超級女聲'],
        }
    }, {
        # record of live streaming
        'url': 'http://www.meipai.com/media/585526361',
        'md5': 'ff7d6afdbc6143342408223d4f5fb99a',
        'info_dict': {
            'id': '585526361',
            'ext': 'mp4',
            'title': '姿昀和善願 練歌練琴啦😁😁😁',
            'description': '姿昀和善願 練歌練琴啦😁😁😁',
            'thumbnail': r're:^https?://.*\.jpg$',
            'duration': 5975,
            'timestamp': 1474311799,
            'upload_date': '20160919',
            'view_count': 1215,
            'creator': '她她-TATA',
        }
    }]

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

        title = self._og_search_title(
            webpage, default=None) or self._html_search_regex(
            r'<title[^>]*>([^<]+)</title>', webpage, 'title')

        formats = []

        # recorded playback of live streaming
        m3u8_url = self._html_search_regex(
            r'file:\s*encodeURIComponent\((["\'])(?P<url>(?:(?!\1).)+)\1\)',
            webpage, 'm3u8 url', group='url', default=None)
        if m3u8_url:
            formats.extend(self._extract_m3u8_formats(
                m3u8_url, video_id, 'mp4', entry_protocol='m3u8_native',
                m3u8_id='hls', fatal=False))

        if not formats:
            # regular uploaded video
            video_url = self._search_regex(
                r'data-video=(["\'])(?P<url>(?:(?!\1).)+)\1', webpage, 'video url',
                group='url', default=None)
            if video_url:
                formats.append({
                    'url': video_url,
                    'format_id': 'http',
                })

        timestamp = unified_timestamp(self._og_search_property(
            'video:release_date', webpage, 'release date', fatal=False))

        tags = self._og_search_property(
            'video:tag', webpage, 'tags', default='').split(',')

        view_count = int_or_none(self._html_search_meta(
            'interactionCount', webpage, 'view count'))
        duration = parse_duration(self._html_search_meta(
            'duration', webpage, 'duration'))
        creator = self._og_search_property(
            'video:director', webpage, 'creator', fatal=False)

        return {
            'id': video_id,
            'title': title,
            'description': self._og_search_description(webpage),
            'thumbnail': self._og_search_thumbnail(webpage),
            'duration': duration,
            'timestamp': timestamp,
            'view_count': view_count,
            'creator': creator,
            'tags': tags,
            'formats': formats,
        }


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

class MelonVODIE(InfoExtractor):
    _VALID_URL = r'https?://vod\.melon\.com/video/detail2\.html?\?.*?mvId=(?P<id>[0-9]+)'
    _TEST = {
        'url': 'http://vod.melon.com/video/detail2.htm?mvId=50158734',
        'info_dict': {
            'id': '50158734',
            'ext': 'mp4',
            'title': "Jessica 'Wonderland' MV Making Film",
            'thumbnail': r're:^https?://.*\.jpg$',
            'artist': 'Jessica (제시카)',
            'upload_date': '20161212',
            'duration': 203,
        },
        'params': {
            'skip_download': 'm3u8 download',
        }
    }

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

        play_info = self._download_json(
            'http://vod.melon.com/video/playerInfo.json', video_id,
            note='Downloading player info JSON', query={'mvId': video_id})

        title = play_info['mvInfo']['MVTITLE']

        info = self._download_json(
            'http://vod.melon.com/delivery/streamingInfo.json', video_id,
            note='Downloading streaming info JSON',
            query={
                'contsId': video_id,
                'contsType': 'VIDEO',
            })

        stream_info = info['streamingInfo']

        formats = self._extract_m3u8_formats(
            stream_info['encUrl'], video_id, 'mp4', m3u8_id='hls')
        self._sort_formats(formats)

        artist_list = play_info.get('artistList')
        artist = None
        if isinstance(artist_list, list):
            artist = ', '.join(
                [a['ARTISTNAMEWEBLIST']
                 for a in artist_list if a.get('ARTISTNAMEWEBLIST')])

        thumbnail = urljoin(info.get('staticDomain'), stream_info.get('imgPath'))

        duration = int_or_none(stream_info.get('playTime'))
        upload_date = stream_info.get('mvSvcOpenDt', '')[:8] or None

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


code:
from .common import InfoExtractor
from .pladform import PladformIE
from ..utils import (
    unescapeHTML,
    int_or_none,
    ExtractorError,
)

class METAIE(InfoExtractor):
    _VALID_URL = r'https?://video\.meta\.ua/(?:iframe/)?(?P<id>[0-9]+)'
    _TESTS = [{
        'url': 'http://video.meta.ua/5502115.video',
        'md5': '71b6f3ee274bef16f1ab410f7f56b476',
        'info_dict': {
            'id': '5502115',
            'ext': 'mp4',
            'title': 'Sony Xperia Z camera test [HQ]',
            'description': 'Xperia Z shoots video in FullHD HDR.',
            'uploader_id': 'nomobile',
            'uploader': 'CHЁZA.TV',
            'upload_date': '20130211',
        },
        'add_ie': ['Youtube'],
    }, {
        'url': 'http://video.meta.ua/iframe/5502115',
        'only_matching': True,
    }, {
        # pladform embed
        'url': 'http://video.meta.ua/7121015.video',
        'only_matching': True,
    }]

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

        st_html5 = self._search_regex(
            r"st_html5\s*=\s*'#([^']+)'", webpage, 'uppod html5 st', default=None)

        if st_html5:
            # uppod st decryption algorithm is reverse engineered from function un(s) at uppod.js
            json_str = ''
            for i in range(0, len(st_html5), 3):
                json_str += '&#x0%s;' % st_html5[i:i + 3]
            uppod_data = self._parse_json(unescapeHTML(json_str), video_id)
            error = uppod_data.get('customnotfound')
            if error:
                raise ExtractorError('%s said: %s' % (self.IE_NAME, error), expected=True)

            video_url = uppod_data['file']
            info = {
                'id': video_id,
                'url': video_url,
                'title': uppod_data.get('comment') or self._og_search_title(webpage),
                'description': self._og_search_description(webpage, default=None),
                'thumbnail': uppod_data.get('poster') or self._og_search_thumbnail(webpage),
                'duration': int_or_none(self._og_search_property(
                    'video:duration', webpage, default=None)),
            }
            if 'youtube.com/' in video_url:
                info.update({
                    '_type': 'url_transparent',
                    'ie_key': 'Youtube',
                })
            return info

        pladform_url = PladformIE._extract_url(webpage)
        if pladform_url:
            return self.url_result(pladform_url)


code:
import json
import re
import urllib.parse

from .common import InfoExtractor
from ..compat import compat_parse_qs, compat_urllib_parse_unquote
from ..utils import (
    ExtractorError,
    determine_ext,
    get_element_by_attribute,
    int_or_none,
    mimetype2ext,
)

class MetacafeIE(InfoExtractor):
    _VALID_URL = r'https?://(?:www\.)?metacafe\.com/watch/(?P<id>[^/]+)/(?P<display_id>[^/?#]+)'
    _DISCLAIMER = 'http://www.metacafe.com/family_filter/'
    _FILTER_POST = 'http://www.metacafe.com/f/index.php?inputType=filter&controllerGroup=user'
    IE_NAME = 'metacafe'
    _TESTS = [
        # Youtube video
        {
            'add_ie': ['Youtube'],
            'url': 'http://metacafe.com/watch/yt-_aUehQsCQtM/the_electric_company_short_i_pbs_kids_go/',
            'info_dict': {
                'id': '_aUehQsCQtM',
                'ext': 'mp4',
                'upload_date': '20090102',
                'title': 'The Electric Company | "Short I" | PBS KIDS GO!',
                'description': 'md5:2439a8ef6d5a70e380c22f5ad323e5a8',
                'uploader': 'PBS',
                'uploader_id': 'PBS'
            }
        },
        # Normal metacafe video
        {
            'url': 'http://www.metacafe.com/watch/11121940/news_stuff_you_wont_do_with_your_playstation_4/',
            'md5': '6e0bca200eaad2552e6915ed6fd4d9ad',
            'info_dict': {
                'id': '11121940',
                'ext': 'mp4',
                'title': 'News: Stuff You Won\'t Do with Your PlayStation 4',
                'uploader': 'ign',
                'description': 'Sony released a massive FAQ on the PlayStation Blog detailing the PS4\'s capabilities and limitations.',
            },
            'skip': 'Page is temporarily unavailable.',
        },
        # metacafe video with family filter
        {
            'url': 'http://www.metacafe.com/watch/2155630/adult_art_by_david_hart_156/',
            'md5': 'b06082c5079bbdcde677a6291fbdf376',
            'info_dict': {
                'id': '2155630',
                'ext': 'mp4',
                'title': 'Adult Art By David Hart 156',
                'uploader': '63346',
                'description': 'md5:9afac8fc885252201ad14563694040fc',
            },
            'params': {
                'skip_download': True,
            },
        },
        # AnyClip video
        {
            'url': 'http://www.metacafe.com/watch/an-dVVXnuY7Jh77J/the_andromeda_strain_1971_stop_the_bomb_part_3/',
            'info_dict': {
                'id': 'an-dVVXnuY7Jh77J',
                'ext': 'mp4',
                'title': 'The Andromeda Strain (1971): Stop the Bomb Part 3',
                'uploader': 'AnyClip',
                'description': 'md5:cbef0460d31e3807f6feb4e7a5952e5b',
            },
        },
        # age-restricted video
        {
            'url': 'http://www.metacafe.com/watch/5186653/bbc_internal_christmas_tape_79_uncensored_outtakes_etc/',
            'md5': '98dde7c1a35d02178e8ab7560fe8bd09',
            'info_dict': {
                'id': '5186653',
                'ext': 'mp4',
                'title': 'BBC INTERNAL Christmas Tape \'79 - UNCENSORED Outtakes, Etc.',
                'uploader': 'Dwayne Pipe',
                'description': 'md5:950bf4c581e2c059911fa3ffbe377e4b',
                'age_limit': 18,
            },
        },
        # cbs video
        {
            'url': 'http://www.metacafe.com/watch/cb-8VD4r_Zws8VP/open_this_is_face_the_nation_february_9/',
            'info_dict': {
                'id': '8VD4r_Zws8VP',
                'ext': 'flv',
                'title': 'Open: This is Face the Nation, February 9',
                'description': 'md5:8a9ceec26d1f7ed6eab610834cc1a476',
                'duration': 96,
                'uploader': 'CBSI-NEW',
                'upload_date': '20140209',
                'timestamp': 1391959800,
            },
            'params': {
                # rtmp download
                'skip_download': True,
            },
        },
        # Movieclips.com video
        {
            'url': 'http://www.metacafe.com/watch/mv-Wy7ZU/my_week_with_marilyn_do_you_love_me/',
            'info_dict': {
                'id': 'mv-Wy7ZU',
                'ext': 'mp4',
                'title': 'My Week with Marilyn - Do You Love Me?',
                'description': 'From the movie My Week with Marilyn - Colin (Eddie Redmayne) professes his love to Marilyn (Michelle Williams) and gets her to promise to return to set and finish the movie.',
                'uploader': 'movie_trailers',
                'duration': 176,
            },
            'params': {
                'skip_download': 'requires rtmpdump',
            }
        }
    ]

    def report_disclaimer(self):
        self.to_screen('Retrieving disclaimer')

    def _real_extract(self, url):
        # Extract id and simplified title from URL
        video_id, display_id = self._match_valid_url(url).groups()

        # the video may come from an external site
        m_external = re.match(r'^(\w{2})-(.*)$', video_id)
        if m_external is not None:
            prefix, ext_id = m_external.groups()
            # Check if video comes from YouTube
            if prefix == 'yt':
                return self.url_result('http://www.youtube.com/watch?v=%s' % ext_id, 'Youtube')
            # CBS videos use theplatform.com
            if prefix == 'cb':
                return self.url_result('theplatform:%s' % ext_id, 'ThePlatform')

        headers = {
            # Disable family filter
            'Cookie': 'user=%s; ' % urllib.parse.quote(json.dumps({'ffilter': False}))
        }

        # AnyClip videos require the flashversion cookie so that we get the link
        # to the mp4 file
        if video_id.startswith('an-'):
            headers['Cookie'] += 'flashVersion=0; '

        # Retrieve video webpage to extract further information
        webpage = self._download_webpage(url, video_id, headers=headers)

        error = get_element_by_attribute(
            'class', 'notfound-page-title', webpage)
        if error:
            raise ExtractorError(error, expected=True)

        video_title = self._html_search_meta(
            ['og:title', 'twitter:title'], webpage, 'title', default=None) or self._search_regex(r'<h1>(.*?)</h1>', webpage, 'title')

        # Extract URL, uploader and title from webpage
        self.report_extraction(video_id)
        video_url = None
        mobj = re.search(r'(?m)&(?:media|video)URL=([^&]+)', webpage)
        if mobj is not None:
            mediaURL = compat_urllib_parse_unquote(mobj.group(1))
            video_ext = determine_ext(mediaURL)

            # Extract gdaKey if available
            mobj = re.search(r'(?m)&gdaKey=(.*?)&', webpage)
            if mobj is None:
                video_url = mediaURL
            else:
                gdaKey = mobj.group(1)
                video_url = '%s?__gda__=%s' % (mediaURL, gdaKey)
        if video_url is None:
            mobj = re.search(r'<video src="([^"]+)"', webpage)
            if mobj:
                video_url = mobj.group(1)
                video_ext = 'mp4'
        if video_url is None:
            flashvars = self._search_regex(
                r' name="flashvars" value="(.*?)"', webpage, 'flashvars',
                default=None)
            if flashvars:
                vardict = compat_parse_qs(flashvars)
                if 'mediaData' not in vardict:
                    raise ExtractorError('Unable to extract media URL')
                mobj = re.search(
                    r'"mediaURL":"(?P<mediaURL>http.*?)",(.*?)"key":"(?P<key>.*?)"', vardict['mediaData'][0])
                if mobj is None:
                    raise ExtractorError('Unable to extract media URL')
                mediaURL = mobj.group('mediaURL').replace('\\/', '/')
                video_url = '%s?__gda__=%s' % (mediaURL, mobj.group('key'))
                video_ext = determine_ext(video_url)
        if video_url is None:
            player_url = self._search_regex(
                r"swfobject\.embedSWF\('([^']+)'",
                webpage, 'config URL', default=None)
            if player_url:
                config_url = self._search_regex(
                    r'config=(.+)$', player_url, 'config URL')
                config_doc = self._download_xml(
                    config_url, video_id,
                    note='Downloading video config')
                smil_url = config_doc.find('.//properties').attrib['smil_file']
                smil_doc = self._download_xml(
                    smil_url, video_id,
                    note='Downloading SMIL document')
                base_url = smil_doc.find('./head/meta').attrib['base']
                video_url = []
                for vn in smil_doc.findall('.//video'):
                    br = int(vn.attrib['system-bitrate'])
                    play_path = vn.attrib['src']
                    video_url.append({
                        'format_id': 'smil-%d' % br,
                        'url': base_url,
                        'play_path': play_path,
                        'page_url': url,
                        'player_url': player_url,
                        'ext': play_path.partition(':')[0],
                    })
        if video_url is None:
            flashvars = self._parse_json(self._search_regex(
                r'flashvars\s*=\s*({.*});', webpage, 'flashvars',
                default=None), video_id, fatal=False)
            if flashvars:
                video_url = []
                for source in flashvars.get('sources'):
                    source_url = source.get('src')
                    if not source_url:
                        continue
                    ext = mimetype2ext(source.get('type')) or determine_ext(source_url)
                    if ext == 'm3u8':
                        video_url.extend(self._extract_m3u8_formats(
                            source_url, video_id, 'mp4',
                            'm3u8_native', m3u8_id='hls', fatal=False))
                    else:
                        video_url.append({
                            'url': source_url,
                            'ext': ext,
                        })

        if video_url is None:
            raise ExtractorError('Unsupported video type')

        description = self._html_search_meta(
            ['og:description', 'twitter:description', 'description'],
            webpage, 'title', fatal=False)
        thumbnail = self._html_search_meta(
            ['og:image', 'twitter:image'], webpage, 'title', fatal=False)
        video_uploader = self._html_search_regex(
            r'submitter=(.*?);|googletag\.pubads\(\)\.setTargeting\("(?:channel|submiter)","([^"]+)"\);',
            webpage, 'uploader nickname', fatal=False)
        duration = int_or_none(
            self._html_search_meta('video:duration', webpage, default=None))
        age_limit = (
            18
            if re.search(r'(?:"contentRating":|"rating",)"restricted"', webpage)
            else 0)

        if isinstance(video_url, list):
            formats = video_url
        else:
            formats = [{
                'url': video_url,
                'ext': video_ext,
            }]
        self._sort_formats(formats)

        return {
            'id': video_id,
            'display_id': display_id,
            'description': description,
            'uploader': video_uploader,
            'title': video_title,
            'thumbnail': thumbnail,
            'age_limit': age_limit,
            'formats': formats,
            'duration': duration,
        }


code:
import re

from .common import InfoExtractor
from ..utils import (
    fix_xml_ampersands,
)

class MetacriticIE(InfoExtractor):
    _VALID_URL = r'https?://(?:www\.)?metacritic\.com/.+?/trailers/(?P<id>\d+)'

    _TESTS = [{
        'url': 'http://www.metacritic.com/game/playstation-4/infamous-second-son/trailers/3698222',
        'info_dict': {
            'id': '3698222',
            'ext': 'mp4',
            'title': 'inFamous: Second Son - inSide Sucker Punch: Smoke & Mirrors',
            'description': 'Take a peak behind-the-scenes to see how Sucker Punch brings smoke into the universe of inFAMOUS Second Son on the PS4.',
            'duration': 221,
        },
        'skip': 'Not providing trailers anymore',
    }, {
        'url': 'http://www.metacritic.com/game/playstation-4/tales-from-the-borderlands-a-telltale-game-series/trailers/5740315',
        'info_dict': {
            'id': '5740315',
            'ext': 'mp4',
            'title': 'Tales from the Borderlands - Finale: The Vault of the Traveler',
            'description': 'In the final episode of the season, all hell breaks loose. Jack is now in control of Helios\' systems, and he\'s ready to reclaim his rightful place as king of Hyperion (with or without you).',
            'duration': 114,
        },
    }]

    def _real_extract(self, url):
        mobj = self._match_valid_url(url)
        video_id = mobj.group('id')
        webpage = self._download_webpage(url, video_id)
        # The xml is not well formatted, there are raw '&'
        info = self._download_xml('http://www.metacritic.com/video_data?video=' + video_id,
                                  video_id, 'Downloading info xml', transform_source=fix_xml_ampersands)

        clip = next(c for c in info.findall('playList/clip') if c.find('id').text == video_id)
        formats = []
        for videoFile in clip.findall('httpURI/videoFile'):
            rate_str = videoFile.find('rate').text
            video_url = videoFile.find('filePath').text
            formats.append({
                'url': video_url,
                'ext': 'mp4',
                'format_id': rate_str,
                'tbr': int(rate_str),
            })
        self._sort_formats(formats)

        description = self._html_search_regex(r'Description:(.*?)
',
                                              webpage, 'description', flags=re.DOTALL)

        return {
            'id': video_id,
            'title': clip.find('title').text,
            'formats': formats,
            'description': description,
            'duration': int(clip.find('duration').text),
        }