code:
from .adobepass import AdobePassIE
from ..utils import (
    update_url_query,
    smuggle_url,
)

class SyfyIE(AdobePassIE):
    _VALID_URL = r'https?://(?:www\.)?syfy\.com/(?:[^/]+/)?videos/(?P<id>[^/?#]+)'
    _TESTS = [{
        'url': 'http://www.syfy.com/theinternetruinedmylife/videos/the-internet-ruined-my-life-season-1-trailer',
        'info_dict': {
            'id': '2968097',
            'ext': 'mp4',
            'title': 'The Internet Ruined My Life: Season 1 Trailer',
            'description': 'One tweet, one post, one click, can destroy everything.',
            'uploader': 'NBCU-MPAT',
            'upload_date': '20170113',
            'timestamp': 1484345640,
        },
        'params': {
            # m3u8 download
            'skip_download': True,
        },
        'add_ie': ['ThePlatform'],
    }]

    def _real_extract(self, url):
        display_id = self._match_id(url)
        webpage = self._download_webpage(url, display_id)
        syfy_mpx = list(self._parse_json(self._search_regex(
            r'jQuery\.extend\(Drupal\.settings\s*,\s*({.+?})\);', webpage, 'drupal settings'),
            display_id)['syfy']['syfy_mpx'].values())[0]
        video_id = syfy_mpx['mpxGUID']
        title = syfy_mpx['episodeTitle']
        query = {
            'mbr': 'true',
            'manifest': 'm3u',
        }
        if syfy_mpx.get('entitlement') == 'auth':
            resource = self._get_mvpd_resource(
                'syfy', title, video_id,
                syfy_mpx.get('mpxRating', 'TV-14'))
            query['auth'] = self._extract_mvpd_auth(
                url, video_id, 'syfy', resource)

        return {
            '_type': 'url_transparent',
            'ie_key': 'ThePlatform',
            'url': smuggle_url(update_url_query(
                self._proto_relative_url(syfy_mpx['releaseURL']), query),
                {'force_smil_url': True}),
            'title': title,
            'id': video_id,
            'display_id': display_id,
        }


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

class SYVDKIE(InfoExtractor):
    _VALID_URL = r'https?://(?:www\.)?24syv\.dk/episode/(?P<id>[\w-]+)'

    _TESTS = [{
        'url': 'https://24syv.dk/episode/isabella-arendt-stiller-op-for-de-konservative-2',
        'md5': '429ce5a423dd4b1e1d0bf3a569558089',
        'info_dict': {
            'id': '12215',
            'display_id': 'isabella-arendt-stiller-op-for-de-konservative-2',
            'ext': 'mp3',
            'title': 'Isabella Arendt stiller op for De Konservative',
            'description': 'md5:f5fa6a431813bf37284f3412ad7c6c06'
        }
    }]

    def _real_extract(self, url):
        video_id = self._match_id(url)
        webpage = self._download_webpage(url, video_id)
        info_data = self._search_nextjs_data(webpage, video_id)['props']['pageProps']['episodeDetails'][0]

        return {
            'id': str(info_data['id']),
            'vcodec': 'none',
            'ext': 'mp3',
            'url': info_data['details']['enclosure'],
            'display_id': video_id,
            'title': traverse_obj(info_data, ('title', 'rendered')),
            'description': traverse_obj(info_data, ('details', 'post_title')),
        }

repo_name: Lesmiscore/ytdl


code:
from .common import InfoExtractor

class SztvHuIE(InfoExtractor):
    _VALID_URL = r'https?://(?:(?:www\.)?sztv\.hu|www\.tvszombathely\.hu)/(?:[^/]+)/.+-(?P<id>[0-9]+)'
    _TEST = {
        'url': 'http://sztv.hu/hirek/cserkeszek-nepszerusitettek-a-kornyezettudatos-eletmodot-a-savaria-teren-20130909',
        'md5': 'a6df607b11fb07d0e9f2ad94613375cb',
        'info_dict': {
            'id': '20130909',
            'ext': 'mp4',
            'title': 'Cserkészek népszerűsítették a környezettudatos életmódot a Savaria téren',
            'description': 'A zöld nap játékos ismeretterjesztő programjait a Magyar Cserkész Szövetség szervezte, akik az ország nyolc városában adják át tudásukat az érdeklődőknek. A PET...',
        },
    }

    def _real_extract(self, url):
        video_id = self._match_id(url)
        webpage = self._download_webpage(url, video_id)
        video_file = self._search_regex(
            r'file: "...:(.*?)",', webpage, 'video file')
        title = self._html_search_regex(
            r'<meta name="title" content="([^"]*?) - [^-]*? - [^-]*?"',
            webpage, 'video title')
        description = self._html_search_regex(
            r'<meta name="description" content="([^"]*)"/>',
            webpage, 'video description', fatal=False)
        thumbnail = self._og_search_thumbnail(webpage)

        video_url = 'http://media.sztv.hu/vod/' + video_file

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


code:
import re

from .common import InfoExtractor
from ..utils import (
    js_to_json,
    extract_attributes,
    try_get,
    int_or_none,
)

class TagesschauIE(InfoExtractor):
    _VALID_URL = r'https?://(?:www\.)?tagesschau\.de/(?P<path>[^/]+/(?:[^/]+/)*?(?P<id>[^/#?]+?(?:-?[0-9]+)?))(?:~_?[^/#?]+?)?\.html'

    _TESTS = [{
        'url': 'http://www.tagesschau.de/multimedia/video/video-102143.html',
        'md5': '7a7287612fa881a1ae1d087df45c2fd6',
        'info_dict': {
            'id': 'video-102143-1',
            'ext': 'mp4',
            'title': 'Regierungsumbildung in Athen: Neue Minister in Griechenland vereidigt',
        },
    }, {
        'url': 'http://www.tagesschau.de/multimedia/sendung/ts-5727.html',
        'md5': '3c54c1f6243d279b706bde660ceec633',
        'info_dict': {
            'id': 'ts-5727-1',
            'ext': 'mp4',
            'title': 'Ganze Sendung',
        },
    }, {
        # exclusive audio
        'url': 'http://www.tagesschau.de/multimedia/audio/audio-29417.html',
        'md5': '4cf22023c285f35e99c24d290ba58cc9',
        'info_dict': {
            'id': 'audio-29417-1',
            'ext': 'mp3',
            'title': 'Brasilianischer Präsident Bolsonaro unter Druck: Corona-Bericht wird vorgestellt',
        },
    }, {
        'url': 'http://www.tagesschau.de/inland/bnd-303.html',
        'md5': '12cfb212d9325b5ba0d52b625f1aa61c',
        'info_dict': {
            'id': 'bnd-303-1',
            'ext': 'mp4',
            'title': 'SPD-Gruppenbild mit Bärbel Bas nach der Fraktionssitzung | dpa',
        },
    }, {
        'url': 'http://www.tagesschau.de/inland/afd-parteitag-135.html',
        'info_dict': {
            'id': 'afd-parteitag-135',
            'title': 'AfD',
        },
        'playlist_count': 20,
    }, {
        'url': 'https://www.tagesschau.de/multimedia/audio/audio-29417~player.html',
        'info_dict': {
            'id': 'audio-29417-1',
            'ext': 'mp3',
            'title': 'Brasilianischer Präsident Bolsonaro unter Druck: Corona-Bericht wird vorgestellt',
        },
    }, {
        'url': 'http://www.tagesschau.de/multimedia/sendung/tsg-3771.html',
        'only_matching': True,
    }, {
        'url': 'http://www.tagesschau.de/multimedia/sendung/tt-3827.html',
        'only_matching': True,
    }, {
        'url': 'http://www.tagesschau.de/multimedia/sendung/nm-3475.html',
        'only_matching': True,
    }, {
        'url': 'http://www.tagesschau.de/multimedia/sendung/weltspiegel-3167.html',
        'only_matching': True,
    }, {
        'url': 'http://www.tagesschau.de/multimedia/tsvorzwanzig-959.html',
        'only_matching': True,
    }, {
        'url': 'http://www.tagesschau.de/multimedia/sendung/bab/bab-3299~_bab-sendung-209.html',
        'only_matching': True,
    }, {
        'url': 'http://www.tagesschau.de/multimedia/video/video-102303~_bab-sendung-211.html',
        'only_matching': True,
    }, {
        'url': 'http://www.tagesschau.de/100sekunden/index.html',
        'only_matching': True,
    }, {
        # playlist article with collapsing sections
        'url': 'http://www.tagesschau.de/wirtschaft/faq-freihandelszone-eu-usa-101.html',
        'only_matching': True,
    }]

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

        webpage = self._download_webpage(url, display_id)

        title = self._html_search_regex(
            r'<span[^>]*class="headline"[^>]*>(.+?)</span>',
            webpage, 'title', default=None) or self._og_search_title(webpage, fatal=False)

        entries = []
        videos = re.findall(r'<div[^>]+>', webpage)
        num = 0
        for video in videos:
            video = extract_attributes(video).get('data-config')
            if not video:
                continue
            video = self._parse_json(video, video_id, transform_source=js_to_json, fatal=False)
            video_formats = try_get(video, lambda x: x['mc']['_mediaArray'][0]['_mediaStreamArray'])
            if not video_formats:
                continue
            num += 1
            for video_format in video_formats:
                media_url = video_format.get('_stream') or ''
                formats = []
                if media_url.endswith('master.m3u8'):
                    formats = self._extract_m3u8_formats(media_url, video_id, 'mp4', m3u8_id='hls')
                elif media_url.endswith('.hi.mp3') and media_url.startswith('https://download'):
                    formats = [{
                        'url': media_url,
                        'vcodec': 'none',
                    }]
                if not formats:
                    continue
                entries.append({
                    'id': '%s-%d' % (display_id, num),
                    'title': try_get(video, lambda x: x['mc']['_title']),
                    'duration': int_or_none(try_get(video, lambda x: x['mc']['_duration'])),
                    'formats': formats
                })
        if len(entries) > 1:
            return self.playlist_result(entries, display_id, title)
        formats = entries[0]['formats']
        video_info = self._search_json_ld(webpage, video_id)
        description = video_info.get('description')
        thumbnail = self._og_search_thumbnail(webpage) or video_info.get('thumbnail')
        timestamp = video_info.get('timestamp')
        title = title or video_info.get('description')

        self._sort_formats(formats)

        return {
            'id': display_id,
            'title': title,
            'thumbnail': thumbnail,
            'formats': formats,
            'timestamp': timestamp,
            'description': description,
        }


code:
import json

from .common import InfoExtractor
from ..utils import (
    js_to_json,
    qualities,
)

class TassIE(InfoExtractor):
    _VALID_URL = r'https?://(?:tass\.ru|itar-tass\.com)/[^/]+/(?P<id>\d+)'
    _TESTS = [
        {
            'url': 'http://tass.ru/obschestvo/1586870',
            'md5': '3b4cdd011bc59174596b6145cda474a4',
            'info_dict': {
                'id': '1586870',
                'ext': 'mp4',
                'title': 'Посетителям московского зоопарка показали красную панду',
                'description': 'Приехавшую из Дублина Зейну можно увидеть в павильоне "Кошки тропиков"',
                'thumbnail': r're:^https?://.*\.jpg$',
            },
        },
        {
            'url': 'http://itar-tass.com/obschestvo/1600009',
            'only_matching': True,
        },
    ]

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

        webpage = self._download_webpage(url, video_id)

        sources = json.loads(js_to_json(self._search_regex(
            r'(?s)sources\s*:\s*(\[.+?\])', webpage, 'sources')))

        quality = qualities(['sd', 'hd'])

        formats = []
        for source in sources:
            video_url = source.get('file')
            if not video_url or not video_url.startswith('http') or not video_url.endswith('.mp4'):
                continue
            label = source.get('label')
            formats.append({
                'url': video_url,
                'format_id': label,
                'quality': quality(label),
            })
        self._sort_formats(formats)

        return {
            'id': video_id,
            'title': self._og_search_title(webpage),
            'description': self._og_search_description(webpage),
            'thumbnail': self._og_search_thumbnail(webpage),
            'formats': formats,
        }


code:
import re

from .turner import TurnerBaseIE
from ..compat import (
    compat_urllib_parse_urlparse,
    compat_parse_qs,
)
from ..utils import (
    float_or_none,
    int_or_none,
    strip_or_none,
)

class TBSIE(TurnerBaseIE):
    _VALID_URL = r'https?://(?:www\.)?(?P<site>tbs|tntdrama)\.com(?P<path>/(?:movies|watchtnt|watchtbs|shows/[^/]+/(?:clips|season-\d+/episode-\d+))/(?P<id>[^/?#]+))'
    _TESTS = [{
        'url': 'http://www.tntdrama.com/shows/the-alienist/clips/monster',
        'info_dict': {
            'id': '8d384cde33b89f3a43ce5329de42903ed5099887',
            'ext': 'mp4',
            'title': 'Monster',
            'description': 'Get a first look at the theatrical trailer for TNT’s highly anticipated new psychological thriller The Alienist, which premieres January 22 on TNT.',
            'timestamp': 1508175329,
            'upload_date': '20171016',
        },
        'params': {
            # m3u8 download
            'skip_download': True,
        }
    }, {
        'url': 'http://www.tbs.com/shows/search-party/season-1/episode-1/explicit-the-mysterious-disappearance-of-the-girl-no-one-knew',
        'only_matching': True,
    }, {
        'url': 'http://www.tntdrama.com/movies/star-wars-a-new-hope',
        'only_matching': True,
    }]

    def _real_extract(self, url):
        site, path, display_id = self._match_valid_url(url).groups()
        webpage = self._download_webpage(url, display_id)
        drupal_settings = self._parse_json(self._search_regex(
            r'<script[^>]+?data-drupal-selector="drupal-settings-json"[^>]*?>({.+?})</script>',
            webpage, 'drupal setting'), display_id)
        isLive = 'watchtnt' in path or 'watchtbs' in path
        video_data = next(v for v in drupal_settings['turner_playlist'] if isLive or v.get('url') == path)

        media_id = video_data['mediaID']
        title = video_data['title']
        tokenizer_query = compat_parse_qs(compat_urllib_parse_urlparse(
            drupal_settings['ngtv_token_url']).query)

        info = self._extract_ngtv_info(
            media_id, tokenizer_query, {
                'url': url,
                'site_name': site[:3].upper(),
                'auth_required': video_data.get('authRequired') == '1' or isLive,
                'is_live': isLive
            })

        thumbnails = []
        for image_id, image in video_data.get('images', {}).items():
            image_url = image.get('url')
            if not image_url or image.get('type') != 'video':
                continue
            i = {
                'id': image_id,
                'url': image_url,
            }
            mobj = re.search(r'(\d+)x(\d+)', image_url)
            if mobj:
                i.update({
                    'width': int(mobj.group(1)),
                    'height': int(mobj.group(2)),
                })
            thumbnails.append(i)

        info.update({
            'id': media_id,
            'title': title,
            'description': strip_or_none(video_data.get('descriptionNoTags') or video_data.get('shortDescriptionNoTags')),
            'duration': float_or_none(video_data.get('duration')) or info.get('duration'),
            'timestamp': int_or_none(video_data.get('created')),
            'season_number': int_or_none(video_data.get('season')),
            'episode_number': int_or_none(video_data.get('episode')),
            'thumbnails': thumbnails,
            'is_live': isLive
        })
        return info


code:
import re

from .common import InfoExtractor
from .wistia import WistiaIE
from ..utils import (
    clean_html,
    ExtractorError,
    int_or_none,
    get_element_by_class,
    strip_or_none,
    urlencode_postdata,
    urljoin,
)

class TeachableBaseIE(InfoExtractor):
    _NETRC_MACHINE = 'teachable'
    _URL_PREFIX = 'teachable:'

    _SITES = {
        # Only notable ones here
        'v1.upskillcourses.com': 'upskill',
        'gns3.teachable.com': 'gns3',
        'academyhacker.com': 'academyhacker',
        'stackskills.com': 'stackskills',
        'market.saleshacker.com': 'saleshacker',
        'learnability.org': 'learnability',
        'edurila.com': 'edurila',
        'courses.workitdaily.com': 'workitdaily',
    }

    _VALID_URL_SUB_TUPLE = (_URL_PREFIX, '|'.join(re.escape(site) for site in _SITES.keys()))

    def _real_initialize(self):
        self._logged_in = False

    def _login(self, site):
        if self._logged_in:
            return

        username, password = self._get_login_info(netrc_machine=self._SITES.get(site, site))
        if username is None:
            return

        login_page, urlh = self._download_webpage_handle(
            'https://%s/sign_in' % site, None,
            'Downloading %s login page' % site)

        def is_logged(webpage):
            return any(re.search(p, webpage) for p in (
                r'class=["\']user-signout',
                r']+\bhref=["\']/sign_out',
                r'Log\s+[Oo]ut\s*<'))

        if is_logged(login_page):
            self._logged_in = True
            return

        login_url = urlh.geturl()

        login_form = self._hidden_inputs(login_page)

        login_form.update({
            'user[email]': username,
            'user[password]': password,
        })

        post_url = self._search_regex(
            r'<form[^>]+action=(["\'])(?P<url>(?:(?!\1).)+)\1', login_page,
            'post url', default=login_url, group='url')

        if not post_url.startswith('http'):
            post_url = urljoin(login_url, post_url)

        response = self._download_webpage(
            post_url, None, 'Logging in to %s' % site,
            data=urlencode_postdata(login_form),
            headers={
                'Content-Type': 'application/x-www-form-urlencoded',
                'Referer': login_url,
            })

        if '>I accept the new Privacy Policy<' in response:
            raise ExtractorError(
                'Unable to login: %s asks you to accept new Privacy Policy. '
                'Go to https://%s/ and accept.' % (site, site), expected=True)

        # Successful login
        if is_logged(response):
            self._logged_in = True
            return

        message = get_element_by_class('alert', response)
        if message is not None:
            raise ExtractorError(
                'Unable to login: %s' % clean_html(message), expected=True)

        raise ExtractorError('Unable to log in')

class TeachableIE(TeachableBaseIE):
    _VALID_URL = r'''(?x)
                    (?:
                        %shttps?://(?P<site_t>[^/]+)|
                        https?://(?:www\.)?(?P<site>%s)
                    )
                    /courses/[^/]+/lectures/(?P<id>\d+)
                    ''' % TeachableBaseIE._VALID_URL_SUB_TUPLE

    _TESTS = [{
        'url': 'https://gns3.teachable.com/courses/gns3-certified-associate/lectures/6842364',
        'info_dict': {
            'id': 'untlgzk1v7',
            'ext': 'bin',
            'title': 'Overview',
            'description': 'md5:071463ff08b86c208811130ea1c2464c',
            'duration': 736.4,
            'timestamp': 1542315762,
            'upload_date': '20181115',
            'chapter': 'Welcome',
            'chapter_number': 1,
        },
        'params': {
            'skip_download': True,
        },
    }, {
        'url': 'http://v1.upskillcourses.com/courses/119763/lectures/1747100',
        'only_matching': True,
    }, {
        'url': 'https://gns3.teachable.com/courses/423415/lectures/6885939',
        'only_matching': True,
    }, {
        'url': 'teachable:https://v1.upskillcourses.com/courses/essential-web-developer-course/lectures/1747100',
        'only_matching': True,
    }]

    @staticmethod
    def _is_teachable(webpage):
        return 'teachableTracker.linker:autoLink' in webpage and re.search(
            r'<link[^>]+href=["\']https?://(?:process\.fs|assets)\.teachablecdn\.com',
            webpage)

    @classmethod
    def _extract_embed_urls(cls, url, webpage):
        if cls._is_teachable(webpage):
            if re.match(r'https?://[^/]+/(?:courses|p)', url):
                yield f'{cls._URL_PREFIX}{url}'
                raise cls.StopExtraction()

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

        self._login(site)

        prefixed = url.startswith(self._URL_PREFIX)
        if prefixed:
            url = url[len(self._URL_PREFIX):]

        webpage = self._download_webpage(url, video_id)

        wistia_urls = WistiaIE._extract_embed_urls(url, webpage)
        if not wistia_urls:
            if any(re.search(p, webpage) for p in (
                    r'class=["\']lecture-contents-locked',
                    r'>\s*Lecture contents locked',
                    r'id=["\']lecture-locked',
                    # https://academy.tailoredtutors.co.uk/courses/108779/lectures/1955313
                    r'class=["\'](?:inner-)?lesson-locked',
                    r'>LESSON LOCKED<')):
                self.raise_login_required('Lecture contents locked')
            raise ExtractorError('Unable to find video URL')

        title = self._og_search_title(webpage, default=None)

        chapter = None
        chapter_number = None
        section_item = self._search_regex(
            r'(?s)(?P

]+\bdata-lecture-id=["\']%s[^>]+>.+?
)' % video_id,
            webpage, 'section item', default=None, group='li')
        if section_item:
            chapter_number = int_or_none(self._search_regex(
                r'data-ss-position=["\'](\d+)', section_item, 'section id',
                default=None))
            if chapter_number is not None:
                sections = []
                for s in re.findall(
                        r'(?s)
]+\bclass=["\']section-title[^>]+>(.+?)
', webpage):
                    section = strip_or_none(clean_html(s))
                    if not section:
                        sections = []
                        break
                    sections.append(section)
                if chapter_number <= len(sections):
                    chapter = sections[chapter_number - 1]

        entries = [{
            '_type': 'url_transparent',
            'url': wistia_url,
            'ie_key': WistiaIE.ie_key(),
            'title': title,
            'chapter': chapter,
            'chapter_number': chapter_number,
        } for wistia_url in wistia_urls]

        return self.playlist_result(entries, video_id, title)

class TeachableCourseIE(TeachableBaseIE):
    _VALID_URL = r'''(?x)
                        (?:
                            %shttps?://(?P<site_t>[^/]+)|
                            https?://(?:www\.)?(?P<site>%s)
                        )
                        /(?:courses|p)/(?:enrolled/)?(?P<id>[^/?#&]+)
                    ''' % TeachableBaseIE._VALID_URL_SUB_TUPLE
    _TESTS = [{
        'url': 'http://v1.upskillcourses.com/courses/essential-web-developer-course/',
        'info_dict': {
            'id': 'essential-web-developer-course',
            'title': 'The Essential Web Developer Course (Free)',
        },
        'playlist_count': 192,
    }, {
        'url': 'http://v1.upskillcourses.com/courses/119763/',
        'only_matching': True,
    }, {
        'url': 'http://v1.upskillcourses.com/courses/enrolled/119763',
        'only_matching': True,
    }, {
        'url': 'https://gns3.teachable.com/courses/enrolled/423415',
        'only_matching': True,
    }, {
        'url': 'teachable:https://learn.vrdev.school/p/gear-vr-developer-mini',
        'only_matching': True,
    }, {
        'url': 'teachable:https://filmsimplified.com/p/davinci-resolve-15-crash-course',
        'only_matching': True,
    }]

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

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

        self._login(site)

        prefixed = url.startswith(self._URL_PREFIX)
        if prefixed:
            prefix = self._URL_PREFIX
            url = url[len(prefix):]

        webpage = self._download_webpage(url, course_id)

        url_base = 'https://%s/' % site

        entries = []

        for mobj in re.finditer(
                r'(?s)(?P

]+class=(["\'])(?:(?!\2).)*?section-item[^>]+>.+?
)',
                webpage):
            li = mobj.group('li')
            if 'fa-youtube-play' not in li and not re.search(r'\d{1,2}:\d{2}', li):
                continue
            lecture_url = self._search_regex(
                r']+href=(["\'])(?P<url>(?:(?!\1).)+)\1', li,
                'lecture url', default=None, group='url')
            if not lecture_url:
                continue
            lecture_id = self._search_regex(
                r'/lectures/(\d+)', lecture_url, 'lecture id', default=None)
            title = self._html_search_regex(
                r']+class=["\']lecture-name[^>]+>([^<]+)', li,
                'title', default=None)
            entry_url = urljoin(url_base, lecture_url)
            if prefixed:
                entry_url = self._URL_PREFIX + entry_url
            entries.append(
                self.url_result(
                    entry_url,
                    ie=TeachableIE.ie_key(), video_id=lecture_id,
                    video_title=clean_html(title)))

        course_title = self._html_search_regex(
            (r'(?s)]+class=["\']course-image[^>]+>\s*<h\d>(.+?)</h',
             r'(?s)<h\d[^>]+class=["\']course-title[^>]+>(.+?)</h'),
            webpage, 'course title', fatal=False)

        return self.playlist_result(entries, course_id, course_title)


code:
import re

from .common import InfoExtractor
from ..utils import (
    determine_ext,
    ExtractorError,
    qualities,
)

class TeacherTubeIE(InfoExtractor):
    IE_NAME = 'teachertube'
    IE_DESC = 'teachertube.com videos'

    _VALID_URL = r'https?://(?:www\.)?teachertube\.com/(viewVideo\.php\?video_id=|music\.php\?music_id=|video/(?:[\da-z-]+-)?|audio/)(?P<id>\d+)'

    _TESTS = [{
        # flowplayer
        'url': 'http://www.teachertube.com/viewVideo.php?video_id=339997',
        'md5': 'f9434ef992fd65936d72999951ee254c',
        'info_dict': {
            'id': '339997',
            'ext': 'mp4',
            'title': 'Measures of dispersion from a frequency table',
            'description': 'Measures of dispersion from a frequency table',
            'thumbnail': r're:https?://.*\.(?:jpg|png)',
        },
    }, {
        # jwplayer
        'url': 'http://www.teachertube.com/music.php?music_id=8805',
        'md5': '01e8352006c65757caf7b961f6050e21',
        'info_dict': {
            'id': '8805',
            'ext': 'mp3',
            'title': 'PER ASPERA AD ASTRA',
            'description': 'RADIJSKA EMISIJA ZRAKOPLOVNE TEHNI?KE ?KOLE P',
        },
    }, {
        # unavailable video
        'url': 'http://www.teachertube.com/video/intro-video-schleicher-297790',
        'only_matching': True,
    }]

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

        error = self._search_regex(
            r'<div\b[^>]+\bclass=["\']msgBox error[^>]+>([^<]+)', webpage,
            'error', default=None)
        if error:
            raise ExtractorError('%s said: %s' % (self.IE_NAME, error), expected=True)

        title = self._html_search_meta('title', webpage, 'title', fatal=True)
        TITLE_SUFFIX = ' - TeacherTube'
        if title.endswith(TITLE_SUFFIX):
            title = title[:-len(TITLE_SUFFIX)].strip()

        description = self._html_search_meta('description', webpage, 'description')
        if description:
            description = description.strip()

        quality = qualities(['mp3', 'flv', 'mp4'])

        media_urls = re.findall(r'data-contenturl="([^"]+)"', webpage)
        media_urls.extend(re.findall(r'var\s+filePath\s*=\s*"([^"]+)"', webpage))
        media_urls.extend(re.findall(r'\'file\'\s*:\s*["\']([^"\']+)["\'],', webpage))

        formats = [
            {
                'url': media_url,
                'quality': quality(determine_ext(media_url))
            } for media_url in set(media_urls)
        ]

        self._sort_formats(formats)

        thumbnail = self._og_search_thumbnail(
            webpage, default=None) or self._html_search_meta(
            'thumbnail', webpage)

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

class TeacherTubeUserIE(InfoExtractor):
    IE_NAME = 'teachertube:user:collection'
    IE_DESC = 'teachertube.com user and collection videos'

    _VALID_URL = r'https?://(?:www\.)?teachertube\.com/(user/profile|collection)/(?P<user>[0-9a-zA-Z]+)/?'

    _MEDIA_RE = r'''(?sx)
        class="?sidebar_thumb_time"?>[0-9:]+</div>
        \s*
        <a\s+href="(https?://(?:www\.)?teachertube\.com/(?:video|audio)/[^"]+)"
    '''
    _TEST = {
        'url': 'http://www.teachertube.com/user/profile/rbhagwati2',
        'info_dict': {
            'id': 'rbhagwati2'
        },
        'playlist_mincount': 179,
    }

    def _real_extract(self, url):
        mobj = self._match_valid_url(url)
        user_id = mobj.group('user')

        urls = []
        webpage = self._download_webpage(url, user_id)
        urls.extend(re.findall(self._MEDIA_RE, webpage))

        pages = re.findall(r'/ajax-user/user-videos/%s\?page=([0-9]+)' % user_id, webpage)[:-1]
        for p in pages:
            more = 'http://www.teachertube.com/ajax-user/user-videos/%s?page=%s' % (user_id, p)
            webpage = self._download_webpage(more, user_id, 'Downloading page %s/%s' % (p, len(pages)))
            video_urls = re.findall(self._MEDIA_RE, webpage)
            urls.extend(video_urls)

        entries = [self.url_result(vurl, 'TeacherTube') for vurl in urls]
        return self.playlist_result(entries, user_id)


code:
from .common import InfoExtractor

class TeachingChannelIE(InfoExtractor):
    _VALID_URL = r'https?://(?:www\.)?teachingchannel\.org/videos?/(?P<id>[^/?&#]+)'

    _TEST = {
        'url': 'https://www.teachingchannel.org/videos/teacher-teaming-evolution',
        'info_dict': {
            'id': '3swwlzkT',
            'ext': 'mp4',
            'title': 'A History of Teaming',
            'description': 'md5:2a9033db8da81f2edffa4c99888140b3',
            'duration': 422,
            'upload_date': '20170316',
            'timestamp': 1489691297,
        },
        'params': {
            'skip_download': True,
        },
        'add_ie': ['JWPlatform'],
    }

    def _real_extract(self, url):
        display_id = self._match_id(url)
        webpage = self._download_webpage(url, display_id)
        mid = self._search_regex(
            r'(?:data-mid=["\']|id=["\']jw-video-player-)([a-zA-Z0-9]{8})',
            webpage, 'media id')

        return self.url_result('jwplatform:' + mid, 'JWPlatform', mid)


code:
import json

from .turner import TurnerBaseIE
from ..utils import (
    determine_ext,
    ExtractorError,
    int_or_none,
    mimetype2ext,
    parse_duration,
    parse_iso8601,
    qualities,
)

class TeamcocoIE(TurnerBaseIE):
    _VALID_URL = r'https?://(?:\w+\.)?teamcoco\.com/(?P<id>([^/]+/)*[^/?#]+)'
    _TESTS = [
        {
            'url': 'http://teamcoco.com/video/mary-kay-remote',
            'md5': '55d532f81992f5c92046ad02fec34d7d',
            'info_dict': {
                'id': '80187',
                'ext': 'mp4',
                'title': 'Conan Becomes A Mary Kay Beauty Consultant',
                'description': 'Mary Kay is perhaps the most trusted name in female beauty, so of course Conan is a natural choice to sell their products.',
                'duration': 495.0,
                'upload_date': '20140402',
                'timestamp': 1396407600,
            }
        }, {
            'url': 'http://teamcoco.com/video/louis-ck-interview-george-w-bush',
            'md5': 'cde9ba0fa3506f5f017ce11ead928f9a',
            'info_dict': {
                'id': '19705',
                'ext': 'mp4',
                'description': 'Louis C.K. got starstruck by George W. Bush, so what? Part one.',
                'title': 'Louis C.K. Interview Pt. 1 11/3/11',
                'duration': 288,
                'upload_date': '20111104',
                'timestamp': 1320405840,
            }
        }, {
            'url': 'http://teamcoco.com/video/timothy-olyphant-drinking-whiskey',
            'info_dict': {
                'id': '88748',
                'ext': 'mp4',
                'title': 'Timothy Olyphant Raises A Toast To “Justified”',
                'description': 'md5:15501f23f020e793aeca761205e42c24',
                'upload_date': '20150415',
                'timestamp': 1429088400,
            },
            'params': {
                'skip_download': True,  # m3u8 downloads
            }
        }, {
            'url': 'http://teamcoco.com/video/full-episode-mon-6-1-joel-mchale-jake-tapper-and-musical-guest-courtney-barnett?playlist=x;eyJ0eXBlIjoidGFnIiwiaWQiOjl9',
            'info_dict': {
                'id': '89341',
                'ext': 'mp4',
                'title': 'Full Episode - Mon. 6/1 - Joel McHale, Jake Tapper, And Musical Guest Courtney Barnett',
                'description': 'Guests: Joel McHale, Jake Tapper, And Musical Guest Courtney Barnett',
            },
            'params': {
                'skip_download': True,  # m3u8 downloads
            },
            'skip': 'This video is no longer available.',
        }, {
            'url': 'http://teamcoco.com/video/the-conan-audiencey-awards-for-04/25/18',
            'only_matching': True,
        }, {
            'url': 'http://teamcoco.com/italy/conan-jordan-schlansky-hit-the-streets-of-florence',
            'only_matching': True,
        }, {
            'url': 'http://teamcoco.com/haiti/conan-s-haitian-history-lesson',
            'only_matching': True,
        }, {
            'url': 'http://teamcoco.com/israel/conan-hits-the-streets-beaches-of-tel-aviv',
            'only_matching': True,
        }, {
            'url': 'https://conan25.teamcoco.com/video/ice-cube-kevin-hart-conan-share-lyft',
            'only_matching': True,
        }
    ]
    _RECORD_TEMPL = '''id
        title
        teaser
        publishOn
        thumb {
          preview
        }
        tags {
          name
        }
        duration
        turnerMediaId
        turnerMediaAuthToken'''

    def _graphql_call(self, query_template, object_type, object_id):
        find_object = 'find' + object_type
        return self._download_json(
            'https://teamcoco.com/graphql', object_id, data=json.dumps({
                'query': query_template % (find_object, object_id)
            }).encode(), headers={
                'Content-Type': 'application/json',
            })['data'][find_object]

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

        response = self._graphql_call('''{
  %%s(slug: "%%s") {
    ... on RecordSlug {
      record {
        %s
      }
    }
    ... on PageSlug {
      child {
        id
      }
    }
    ... on NotFoundSlug {
      status
    }
  }
}''' % self._RECORD_TEMPL, 'Slug', display_id)
        if response.get('status'):
            raise ExtractorError('This video is no longer available.', expected=True)

        child = response.get('child')
        if child:
            record = self._graphql_call('''{
  %%s(id: "%%s") {
    ... on Video {
      %s
    }
  }
}''' % self._RECORD_TEMPL, 'Record', child['id'])
        else:
            record = response['record']
        video_id = record['id']

        info = {
            'id': video_id,
            'display_id': display_id,
            'title': record['title'],
            'thumbnail': record.get('thumb', {}).get('preview'),
            'description': record.get('teaser'),
            'duration': parse_duration(record.get('duration')),
            'timestamp': parse_iso8601(record.get('publishOn')),
        }

        media_id = record.get('turnerMediaId')
        if media_id:
            self._initialize_geo_bypass({
                'countries': ['US'],
            })
            info.update(self._extract_ngtv_info(media_id, {
                'accessToken': record['turnerMediaAuthToken'],
                'accessTokenType': 'jws',
            }))
        else:
            video_sources = self._download_json(
                'https://teamcoco.com/_truman/d/' + video_id,
                video_id)['meta']['src']
            if isinstance(video_sources, dict):
                video_sources = video_sources.values()

            formats = []
            get_quality = qualities(['low', 'sd', 'hd', 'uhd'])
            for src in video_sources:
                if not isinstance(src, dict):
                    continue
                src_url = src.get('src')
                if not src_url:
                    continue
                format_id = src.get('label')
                ext = determine_ext(src_url, mimetype2ext(src.get('type')))
                if format_id == 'hls' or ext == 'm3u8':
                    # compat_urllib_parse.urljoin does not work here
                    if src_url.startswith('/'):
                        src_url = 'http://ht.cdn.turner.com/tbs/big/teamcoco' + src_url
                    formats.extend(self._extract_m3u8_formats(
                        src_url, video_id, 'mp4', m3u8_id=format_id, fatal=False))
                else:
                    if src_url.startswith('/mp4:protected/'):
                        # TODO Correct extraction for these files
                        continue
                    tbr = int_or_none(self._search_regex(
                        r'(\d+)k\.mp4', src_url, 'tbr', default=None))

                    formats.append({
                        'url': src_url,
                        'ext': ext,
                        'tbr': tbr,
                        'format_id': format_id,
                        'quality': get_quality(format_id),
                    })
            self._sort_formats(formats)
            info['formats'] = formats

        return info


code:
import re

from .common import InfoExtractor
from ..utils import (
    clean_html,
    determine_ext,
    ExtractorError,
    float_or_none,
    get_element_by_class,
    get_element_by_id,
    parse_duration,
    remove_end,
    urlencode_postdata,
    urljoin,
)

class TeamTreeHouseIE(InfoExtractor):
    _VALID_URL = r'https?://(?:www\.)?teamtreehouse\.com/library/(?P<id>[^/]+)'
    _TESTS = [{
        # Course
        'url': 'https://teamtreehouse.com/library/introduction-to-user-authentication-in-php',
        'info_dict': {
            'id': 'introduction-to-user-authentication-in-php',
            'title': 'Introduction to User Authentication in PHP',
            'description': 'md5:405d7b4287a159b27ddf30ca72b5b053',
        },
        'playlist_mincount': 24,
    }, {
        # WorkShop
        'url': 'https://teamtreehouse.com/library/deploying-a-react-app',
        'info_dict': {
            'id': 'deploying-a-react-app',
            'title': 'Deploying a React App',
            'description': 'md5:10a82e3ddff18c14ac13581c9b8e5921',
        },
        'playlist_mincount': 4,
    }, {
        # Video
        'url': 'https://teamtreehouse.com/library/application-overview-2',
        'info_dict': {
            'id': 'application-overview-2',
            'ext': 'mp4',
            'title': 'Application Overview',
            'description': 'md5:4b0a234385c27140a4378de5f1e15127',
        },
        'expected_warnings': ['This is just a preview'],
    }]
    _NETRC_MACHINE = 'teamtreehouse'

    def _perform_login(self, username, password):

        signin_page = self._download_webpage(
            'https://teamtreehouse.com/signin',
            None, 'Downloading signin page')
        data = self._form_hidden_inputs('new_user_session', signin_page)
        data.update({
            'user_session[email]': username,
            'user_session[password]': password,
        })
        error_message = get_element_by_class('error-message', self._download_webpage(
            'https://teamtreehouse.com/person_session',
            None, 'Logging in', data=urlencode_postdata(data)))
        if error_message:
            raise ExtractorError(clean_html(error_message), expected=True)

    def _real_extract(self, url):
        display_id = self._match_id(url)
        webpage = self._download_webpage(url, display_id)
        title = self._html_search_meta(['og:title', 'twitter:title'], webpage)
        description = self._html_search_meta(
            ['description', 'og:description', 'twitter:description'], webpage)
        entries = self._parse_html5_media_entries(url, webpage, display_id)
        if entries:
            info = entries[0]

            for subtitles in info.get('subtitles', {}).values():
                for subtitle in subtitles:
                    subtitle['ext'] = determine_ext(subtitle['url'], 'srt')

            is_preview = 'data-preview="true"' in webpage
            if is_preview:
                self.report_warning(
                    'This is just a preview. You need to be signed in with a Basic account to download the entire video.', display_id)
                duration = 30
            else:
                duration = float_or_none(self._search_regex(
                    r'data-duration="(\d+)"', webpage, 'duration'), 1000)
                if not duration:
                    duration = parse_duration(get_element_by_id(
                        'video-duration', webpage))

            info.update({
                'id': display_id,
                'title': title,
                'description': description,
                'duration': duration,
            })
            return info
        else:
            def extract_urls(html, extract_info=None):
                for path in re.findall(r']+href="([^"]+)"', html):
                    page_url = urljoin(url, path)
                    entry = {
                        '_type': 'url_transparent',
                        'id': self._match_id(page_url),
                        'url': page_url,
                        'id_key': self.ie_key(),
                    }
                    if extract_info:
                        entry.update(extract_info)
                    entries.append(entry)

            workshop_videos = self._search_regex(
                r'(?s)
]+id="workshop-videos"[^>]*>(.+?)
',
                webpage, 'workshop videos', default=None)
            if workshop_videos:
                extract_urls(workshop_videos)
            else:
                stages_path = self._search_regex(
                    r'(?s)
]+id="syllabus-stages"[^>]+data-url="([^"]+)"',
                    webpage, 'stages path')
                if stages_path:
                    stages_page = self._download_webpage(
                        urljoin(url, stages_path), display_id, 'Downloading stages page')
                    for chapter_number, (chapter, steps_list) in enumerate(re.findall(r'(?s)
]*>\s*(.+?)\s*
.+?
]*>(.+?)
', stages_page), 1):
                        extract_urls(steps_list, {
                            'chapter': chapter,
                            'chapter_number': chapter_number,
                        })
                    title = remove_end(title, ' Course')

            return self.playlist_result(
                entries, display_id, title, description)


code:
import re

from .common import InfoExtractor
from ..utils import (
    get_element_by_attribute,
    clean_html,
)

class TechTalksIE(InfoExtractor):
    _VALID_URL = r'https?://techtalks\.tv/talks/(?:[^/]+/)?(?P<id>\d+)'

    _TESTS = [{
        'url': 'http://techtalks.tv/talks/learning-topic-models-going-beyond-svd/57758/',
        'info_dict': {
            'id': '57758',
            'title': 'Learning Topic Models --- Going beyond SVD',
        },
        'playlist': [
            {
                'info_dict': {
                    'id': '57758',
                    'ext': 'flv',
                    'title': 'Learning Topic Models --- Going beyond SVD',
                },
            },
            {
                'info_dict': {
                    'id': '57758-slides',
                    'ext': 'flv',
                    'title': 'Learning Topic Models --- Going beyond SVD',
                },
            },
        ],
        'params': {
            # rtmp download
            'skip_download': True,
        },
    }, {
        'url': 'http://techtalks.tv/talks/57758',
        'only_matching': True,
    }]

    def _real_extract(self, url):
        mobj = self._match_valid_url(url)
        talk_id = mobj.group('id')
        webpage = self._download_webpage(url, talk_id)
        rtmp_url = self._search_regex(
            r'netConnectionUrl: \'(.*?)\'', webpage, 'rtmp url')
        play_path = self._search_regex(
            r'href=\'(.*?)\' [^>]*id="flowplayer_presenter"',
            webpage, 'presenter play path')
        title = clean_html(get_element_by_attribute('class', 'title', webpage))
        video_info = {
            'id': talk_id,
            'title': title,
            'url': rtmp_url,
            'play_path': play_path,
            'ext': 'flv',
        }
        m_slides = re.search(r'<a class="slides" href=\'(.*?)\'', webpage)
        if m_slides is None:
            return video_info
        else:
            return {
                '_type': 'playlist',
                'id': talk_id,
                'title': title,
                'entries': [
                    video_info,
                    # The slides video
                    {
                        'id': talk_id + '-slides',
                        'title': title,
                        'url': rtmp_url,
                        'play_path': m_slides.group(1),
                        'ext': 'flv',
                    },
                ],
            }


code:
import itertools
import re

from .common import InfoExtractor

from ..utils import (
    int_or_none,
    str_to_int,
    try_get,
    url_or_none,
    unified_strdate,
    parse_duration,
)

class TedBaseIE(InfoExtractor):
    _VALID_URL_BASE = r'https?://www\.ted\.com/(?:{type})(?:/lang/[^/#?]+)?/(?P<id>[\w-]+)'

    def _parse_playlist(self, playlist):
        for entry in try_get(playlist, lambda x: x['videos']['nodes'], list):
            if entry.get('__typename') == 'Video' and entry.get('canonicalUrl'):
                yield self.url_result(entry['canonicalUrl'], TedTalkIE.ie_key())

class TedTalkIE(TedBaseIE):
    _VALID_URL = TedBaseIE._VALID_URL_BASE.format(type='talks')
    _TESTS = [{
        'url': 'https://www.ted.com/talks/candace_parker_how_to_break_down_barriers_and_not_accept_limits',
        'md5': '47e82c666d9c3261d4fe74748a90aada',
        'info_dict': {
            'id': '86532',
            'ext': 'mp4',
            'title': 'How to break down barriers and not accept limits',
            'description': 'md5:000707cece219d1e165b11550d612331',
            'view_count': int,
            'tags': ['personal growth', 'equality', 'activism', 'motivation', 'social change', 'sports'],
            'uploader': 'Candace Parker',
            'duration': 676.0,
            'upload_date': '20220114',
            'release_date': '20211201',
            'thumbnail': r're:http.*\.jpg',
        },
    }]

    def _real_extract(self, url):
        display_id = self._match_id(url)
        webpage = self._download_webpage(url, display_id)
        talk_info = self._search_nextjs_data(webpage, display_id)['props']['pageProps']['videoData']
        video_id = talk_info['id']
        playerData = self._parse_json(talk_info.get('playerData'), video_id)

        http_url = None
        formats, subtitles = [], {}
        for format_id, resources in (playerData.get('resources') or {}).items():
            if format_id == 'hls':
                stream_url = url_or_none(try_get(resources, lambda x: x['stream']))
                if not stream_url:
                    continue
                m3u8_formats, m3u8_subs = self._extract_m3u8_formats_and_subtitles(
                    stream_url, video_id, 'mp4', m3u8_id=format_id, fatal=False)
                formats.extend(m3u8_formats)
                subtitles = self._merge_subtitles(subtitles, m3u8_subs)
                continue

            if not isinstance(resources, list):
                continue
            if format_id == 'h264':
                for resource in resources:
                    h264_url = resource.get('file')
                    if not h264_url:
                        continue
                    bitrate = int_or_none(resource.get('bitrate'))
                    formats.append({
                        'url': h264_url,
                        'format_id': '%s-%sk' % (format_id, bitrate),
                        'tbr': bitrate,
                    })
                    if re.search(r'\d+k', h264_url):
                        http_url = h264_url
            elif format_id == 'rtmp':
                streamer = talk_info.get('streamer')
                if not streamer:
                    continue
                formats.extend({
                    'format_id': '%s-%s' % (format_id, resource.get('name')),
                    'url': streamer,
                    'play_path': resource['file'],
                    'ext': 'flv',
                    'width': int_or_none(resource.get('width')),
                    'height': int_or_none(resource.get('height')),
                    'tbr': int_or_none(resource.get('bitrate')),
                } for resource in resources if resource.get('file'))

        if http_url:
            m3u8_formats = [f for f in formats if f.get('protocol') == 'm3u8' and f.get('vcodec') != 'none']
            for m3u8_format in m3u8_formats:
                bitrate = self._search_regex(r'(\d+k)', m3u8_format['url'], 'bitrate', default=None)
                if not bitrate:
                    continue
                bitrate_url = re.sub(r'\d+k', bitrate, http_url)
                if not self._is_valid_url(
                        bitrate_url, video_id, '%s bitrate' % bitrate):
                    continue
                f = m3u8_format.copy()
                f.update({
                    'url': bitrate_url,
                    'format_id': m3u8_format['format_id'].replace('hls', 'http'),
                    'protocol': 'http',
                })
                if f.get('acodec') == 'none':
                    del f['acodec']
                formats.append(f)

        audio_download = talk_info.get('audioDownload')
        if audio_download:
            formats.append({
                'url': audio_download,
                'format_id': 'audio',
                'vcodec': 'none',
            })

        if not formats:
            external = playerData.get('external') or {}
            service = external.get('service') or ''
            ext_url = external.get('code') if service.lower() == 'youtube' else None
            return self.url_result(ext_url or external['uri'])

        self._sort_formats(formats)

        thumbnail = playerData.get('thumb') or self._og_search_property('image', webpage)
        if thumbnail:
            # trim thumbnail resize parameters
            thumbnail = thumbnail.split('?')[0]

        return {
            'id': video_id,
            'title': talk_info.get('title') or self._og_search_title(webpage),
            'uploader': talk_info.get('presenterDisplayName'),
            'thumbnail': thumbnail,
            'description': talk_info.get('description') or self._og_search_description(webpage),
            'subtitles': subtitles,
            'formats': formats,
            'duration': talk_info.get('duration') or parse_duration(self._og_search_property('video:duration', webpage)),
            'view_count': str_to_int(talk_info.get('viewedCount')),
            'upload_date': unified_strdate(talk_info.get('publishedAt')),
            'release_date': unified_strdate(talk_info.get('recordedOn')),
            'tags': try_get(playerData, lambda x: x['targeting']['tag'].split(',')),
        }

class TedSeriesIE(TedBaseIE):
    _VALID_URL = fr'{TedBaseIE._VALID_URL_BASE.format(type=r"series")}(?:#season_(?P<season>\d+))?'
    _TESTS = [{
        'url': 'https://www.ted.com/series/small_thing_big_idea',
        'info_dict': {
            'id': '3',
            'title': 'Small Thing Big Idea',
            'series': 'Small Thing Big Idea',
            'description': 'md5:6869ca52cec661aef72b3e9f7441c55c'
        },
        'playlist_mincount': 16,
    }, {
        'url': 'https://www.ted.com/series/the_way_we_work#season_2',
        'info_dict': {
            'id': '8_2',
            'title': 'The Way We Work Season 2',
            'series': 'The Way We Work',
            'description': 'md5:59469256e533e1a48c4aa926a382234c',
            'season_number': 2
        },
        'playlist_mincount': 8,
    }]

    def _real_extract(self, url):
        display_id, season = self._match_valid_url(url).group('id', 'season')
        webpage = self._download_webpage(url, display_id, 'Downloading series webpage')
        info = self._search_nextjs_data(webpage, display_id)['props']['pageProps']

        entries = itertools.chain.from_iterable(
            self._parse_playlist(s) for s in info['seasons'] if season in [None, s.get('seasonNumber')])

        series_id = try_get(info, lambda x: x['series']['id'])
        series_name = try_get(info, lambda x: x['series']['name']) or self._og_search_title(webpage, fatal=False)

        return self.playlist_result(
            entries,
            f'{series_id}_{season}' if season and series_id else series_id,
            f'{series_name} Season {season}' if season else series_name,
            self._og_search_description(webpage),
            series=series_name, season_number=int_or_none(season))

class TedPlaylistIE(TedBaseIE):
    _VALID_URL = TedBaseIE._VALID_URL_BASE.format(type=r'playlists(?:/\d+)?')
    _TESTS = [{
        'url': 'https://www.ted.com/playlists/171/the_most_popular_talks_of_all',
        'info_dict': {
            'id': '171',
            'title': 'The most popular talks of all time',
            'description': 'md5:d2f22831dc86c7040e733a3cb3993d78'
        },
        'playlist_mincount': 25,
    }]

    def _real_extract(self, url):
        display_id = self._match_id(url)
        webpage = self._download_webpage(url, display_id)
        playlist = self._search_nextjs_data(webpage, display_id)['props']['pageProps']['playlist']

        return self.playlist_result(
            self._parse_playlist(playlist), playlist.get('id'),
            playlist.get('title') or self._og_search_title(webpage, default='').replace(' | TED Talks', '') or None,
            self._og_search_description(webpage))

class TedEmbedIE(InfoExtractor):
    _VALID_URL = r'https?://embed(?:-ssl)?\.ted\.com/'
    _EMBED_REGEX = [rf'<iframe[^>]+?src=(["\'])(?P<url>{_VALID_URL}.+?)\1']

    _TESTS = [{
        'url': 'https://embed.ted.com/talks/janet_stovall_how_to_get_serious_about_diversity_and_inclusion_in_the_workplace',
        'info_dict': {
            'id': '21802',
            'ext': 'mp4',
            'title': 'How to get serious about diversity and inclusion in the workplace',
            'description': 'md5:0978aafe396e05341f8ecc795d22189d',
            'view_count': int,
            'tags': list,
            'uploader': 'Janet Stovall',
            'duration': 664.0,
            'upload_date': '20180822',
            'release_date': '20180719',
            'thumbnail': r're:http.*\.jpg',
        },
    }]

    def _real_extract(self, url):
        return self.url_result(re.sub(r'://embed(-ssl)?', '://www', url), TedTalkIE.ie_key())


code:
from .common import InfoExtractor
from .youtube import YoutubeIE
from ..utils import (
    js_to_json,
    qualities,
    determine_ext,
)

class Tele13IE(InfoExtractor):
    _VALID_URL = r'^https?://(?:www\.)?t13\.cl/videos(?:/[^/]+)+/(?P<id>[\w-]+)'
    _TESTS = [
        {
            'url': 'http://www.t13.cl/videos/actualidad/el-circulo-de-hierro-de-michelle-bachelet-en-su-regreso-a-la-moneda',
            'md5': '4cb1fa38adcad8fea88487a078831755',
            'info_dict': {
                'id': 'el-circulo-de-hierro-de-michelle-bachelet-en-su-regreso-a-la-moneda',
                'ext': 'mp4',
                'title': 'El círculo de hierro de Michelle Bachelet en su regreso a La Moneda',
            },
            'params': {
                # HTTP Error 404: Not Found
                'skip_download': True,
            },
        },
        {
            'url': 'http://www.t13.cl/videos/mundo/tendencias/video-captan-misteriosa-bola-fuego-cielos-bangkok',
            'md5': '867adf6a3b3fef932c68a71d70b70946',
            'info_dict': {
                'id': 'rOoKv2OMpOw',
                'ext': 'mp4',
                'title': 'Shooting star seen on 7-Sep-2015',
                'description': 'md5:7292ff2a34b2f673da77da222ae77e1e',
                'uploader': 'Porjai Jaturongkhakun',
                'upload_date': '20150906',
                'uploader_id': 'UCnLY_3ezwNcDSC_Wc6suZxw',
            },
            'add_ie': ['Youtube'],
        }
    ]

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

        setup_js = self._search_regex(
            r"(?s)jwplayer\('player-vivo'\).setup\((\{.*?\})\)",
            webpage, 'setup code')
        sources = self._parse_json(self._search_regex(
            r'sources\s*:\s*(\[[^\]]+\])', setup_js, 'sources'),
            display_id, js_to_json)

        preference = qualities(['Móvil', 'SD', 'HD'])
        formats = []
        urls = []
        for f in sources:
            format_url = f['file']
            if format_url and format_url not in urls:
                ext = determine_ext(format_url)
                if ext == 'm3u8':
                    formats.extend(self._extract_m3u8_formats(
                        format_url, display_id, 'mp4', 'm3u8_native',
                        m3u8_id='hls', fatal=False))
                elif YoutubeIE.suitable(format_url):
                    return self.url_result(format_url, 'Youtube')
                else:
                    formats.append({
                        'url': format_url,
                        'format_id': f.get('label'),
                        'quality': preference(f.get('label')),
                        'ext': ext,
                    })
                urls.append(format_url)
        self._sort_formats(formats)

        return {
            'id': display_id,
            'title': self._search_regex(
                r'title\s*:\s*"([^"]+)"', setup_js, 'title'),
            'description': self._html_search_meta(
                'description', webpage, 'description'),
            'thumbnail': self._search_regex(
                r'image\s*:\s*"([^"]+)"', setup_js, 'thumbnail', default=None),
            'formats': formats,
        }


code:
from .dplay import DPlayIE
from ..compat import compat_urlparse
from ..utils import (
    ExtractorError,
    extract_attributes,
)

class Tele5IE(DPlayIE):
    _VALID_URL = r'https?://(?:www\.)?tele5\.de/(?:[^/]+/)*(?P<id>[^/?#&]+)'
    _GEO_COUNTRIES = ['DE']
    _TESTS = [{
        'url': 'https://www.tele5.de/mediathek/filme-online/videos?vid=1549416',
        'info_dict': {
            'id': '1549416',
            'ext': 'mp4',
            'upload_date': '20180814',
            'timestamp': 1534290623,
            'title': 'Pandorum',
        },
        'params': {
            'skip_download': True,
        },
        'skip': 'No longer available: "404 Seite nicht gefunden"',
    }, {
        # jwplatform, nexx unavailable
        'url': 'https://www.tele5.de/filme/ghoul-das-geheimnis-des-friedhofmonsters/',
        'info_dict': {
            'id': 'WJuiOlUp',
            'ext': 'mp4',
            'upload_date': '20200603',
            'timestamp': 1591214400,
            'title': 'Ghoul - Das Geheimnis des Friedhofmonsters',
            'description': 'md5:42002af1d887ff3d5b2b3ca1f8137d97',
        },
        'params': {
            'skip_download': True,
        },
        'skip': 'No longer available, redirects to Filme page',
    }, {
        'url': 'https://tele5.de/mediathek/angel-of-mine/',
        'info_dict': {
            'id': '1252360',
            'ext': 'mp4',
            'upload_date': '20220109',
            'timestamp': 1641762000,
            'title': 'Angel of Mine',
            'description': 'md5:a72546a175e1286eb3251843a52d1ad7',
        },
        'params': {
            'format': 'bestvideo',
        },
    }, {
        'url': 'https://www.tele5.de/kalkofes-mattscheibe/video-clips/politik-und-gesellschaft?ve_id=1551191',
        'only_matching': True,
    }, {
        'url': 'https://www.tele5.de/video-clip/?ve_id=1609440',
        'only_matching': True,
    }, {
        'url': 'https://www.tele5.de/filme/schlefaz-dragon-crusaders/',
        'only_matching': True,
    }, {
        'url': 'https://www.tele5.de/filme/making-of/avengers-endgame/',
        'only_matching': True,
    }, {
        'url': 'https://www.tele5.de/star-trek/raumschiff-voyager/ganze-folge/das-vinculum/',
        'only_matching': True,
    }, {
        'url': 'https://www.tele5.de/anders-ist-sevda/',
        'only_matching': True,
    }]

    def _real_extract(self, url):
        video_id = self._match_id(url)
        webpage = self._download_webpage(url, video_id)
        player_element = self._search_regex(r'(<hyoga-player\b[^>]+?>)', webpage, 'video player')
        player_info = extract_attributes(player_element)
        asset_id, country, realm = (player_info[x] for x in ('assetid', 'locale', 'realm', ))
        endpoint = compat_urlparse.urlparse(player_info['endpoint']).hostname
        source_type = player_info.get('sourcetype')
        if source_type:
            endpoint = '%s-%s' % (source_type, endpoint)
        try:
            return self._get_disco_api_info(url, asset_id, endpoint, realm, country)
        except ExtractorError as e:
            if getattr(e, 'message', '') == 'Missing deviceId in context':
                self.report_drm(video_id)
            raise


code:
import re

from .common import InfoExtractor

class TeleBruxellesIE(InfoExtractor):
    _VALID_URL = r'https?://(?:www\.)?(?:telebruxelles|bx1)\.be/(?:[^/]+/)*(?P<id>[^/#?]+)'
    _TESTS = [{
        'url': 'http://bx1.be/news/que-risque-lauteur-dune-fausse-alerte-a-la-bombe/',
        'md5': 'a2a67a5b1c3e8c9d33109b902f474fd9',
        'info_dict': {
            'id': '158856',
            'display_id': 'que-risque-lauteur-dune-fausse-alerte-a-la-bombe',
            'ext': 'mp4',
            'title': 'Que risque l’auteur d’une fausse alerte à la bombe ?',
            'description': 'md5:3cf8df235d44ebc5426373050840e466',
        },
    }, {
        'url': 'http://bx1.be/sport/futsal-schaerbeek-sincline-5-3-a-thulin/',
        'md5': 'dfe07ecc9c153ceba8582ac912687675',
        'info_dict': {
            'id': '158433',
            'display_id': 'futsal-schaerbeek-sincline-5-3-a-thulin',
            'ext': 'mp4',
            'title': 'Futsal : Schaerbeek s’incline 5-3 à Thulin',
            'description': 'md5:fd013f1488d5e2dceb9cebe39e2d569b',
        },
    }, {
        'url': 'http://bx1.be/emission/bxenf1-gastronomie/',
        'only_matching': True,
    }, {
        'url': 'https://bx1.be/berchem-sainte-agathe/personnel-carrefour-de-berchem-sainte-agathe-inquiet/',
        'only_matching': True,
    }, {
        'url': 'https://bx1.be/dernier-jt/',
        'only_matching': True,
    }, {
        # live stream
        'url': 'https://bx1.be/lives/direct-tv/',
        'only_matching': True,
    }]

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

        article_id = self._html_search_regex(
            r'<article[^>]+\bid=["\']post-(\d+)', webpage, 'article ID', default=None)
        title = self._html_search_regex(
            r'<h1[^>]*>(.+?)</h1>', webpage, 'title',
            default=None) or self._og_search_title(webpage)
        description = self._og_search_description(webpage, default=None)

        rtmp_url = self._html_search_regex(
            r'file["\']?\s*:\s*"(r(?:tm|mt)ps?://[^/]+/(?:vod/mp4:"\s*\+\s*"[^"]+"\s*\+\s*"\.mp4|stream/live))"',
            webpage, 'RTMP url')
        # Yes, they have a typo in scheme name for live stream URLs (e.g.
        # https://bx1.be/lives/direct-tv/)
        rtmp_url = re.sub(r'^rmtp', 'rtmp', rtmp_url)
        rtmp_url = re.sub(r'"\s*\+\s*"', '', rtmp_url)
        formats = self._extract_wowza_formats(rtmp_url, article_id or display_id)
        self._sort_formats(formats)

        is_live = 'stream/live' in rtmp_url

        return {
            'id': article_id or display_id,
            'display_id': display_id,
            'title': title,
            'description': description,
            'formats': formats,
            'is_live': is_live,
        }


code:
import json
import re

from .common import InfoExtractor
from ..utils import (
    clean_html,
    int_or_none,
    str_or_none,
    try_get,
)

class TelecincoIE(InfoExtractor):
    IE_DESC = 'telecinco.es, cuatro.com and mediaset.es'
    _VALID_URL = r'https?://(?:www\.)?(?:telecinco\.es|cuatro\.com|mediaset\.es)/(?:[^/]+/)+(?P<id>.+?)\.html'

    _TESTS = [{
        'url': 'http://www.telecinco.es/robinfood/temporada-01/t01xp14/Bacalao-cocochas-pil-pil_0_1876350223.html',
        'info_dict': {
            'id': '1876350223',
            'title': 'Bacalao con kokotxas al pil-pil',
            'description': 'md5:716caf5601e25c3c5ab6605b1ae71529',
        },
        'playlist': [{
            'md5': '7ee56d665cfd241c0e6d80fd175068b0',
            'info_dict': {
                'id': 'JEA5ijCnF6p5W08A1rNKn7',
                'ext': 'mp4',
                'title': 'Con Martín Berasategui, hacer un bacalao al pil-pil es fácil y divertido',
                'duration': 662,
            },
        }]
    }, {
        'url': 'http://www.cuatro.com/deportes/futbol/barcelona/Leo_Messi-Champions-Roma_2_2052780128.html',
        'md5': 'c86fe0d99e3bdb46b7950d38bf6ef12a',
        'info_dict': {
            'id': 'jn24Od1zGLG4XUZcnUnZB6',
            'ext': 'mp4',
            'title': '¿Quién es este ex futbolista con el que hablan Leo Messi y Luis Suárez?',
            'description': 'md5:a62ecb5f1934fc787107d7b9a2262805',
            'duration': 79,
        },
    }, {
        'url': 'http://www.mediaset.es/12meses/campanas/doylacara/conlatratanohaytrato/Ayudame-dar-cara-trata-trato_2_1986630220.html',
        'md5': 'eddb50291df704ce23c74821b995bcac',
        'info_dict': {
            'id': 'aywerkD2Sv1vGNqq9b85Q2',
            'ext': 'mp4',
            'title': '#DOYLACARA. Con la trata no hay trato',
            'description': 'md5:2771356ff7bfad9179c5f5cd954f1477',
            'duration': 50,
        },
    }, {
        # video in opening's content
        'url': 'https://www.telecinco.es/vivalavida/fiorella-sobrina-edmundo-arrocet-entrevista_18_2907195140.html',
        'info_dict': {
            'id': '2907195140',
            'title': 'La surrealista entrevista a la sobrina de Edmundo Arrocet: "No puedes venir aquí y tomarnos por tontos"',
            'description': 'md5:73f340a7320143d37ab895375b2bf13a',
        },
        'playlist': [{
            'md5': 'adb28c37238b675dad0f042292f209a7',
            'info_dict': {
                'id': 'TpI2EttSDAReWpJ1o0NVh2',
                'ext': 'mp4',
                'title': 'La surrealista entrevista a la sobrina de Edmundo Arrocet: "No puedes venir aquí y tomarnos por tontos"',
                'duration': 1015,
            },
        }],
        'params': {
            'skip_download': True,
        },
    }, {
        'url': 'http://www.telecinco.es/informativos/nacional/Pablo_Iglesias-Informativos_Telecinco-entrevista-Pedro_Piqueras_2_1945155182.html',
        'only_matching': True,
    }, {
        'url': 'http://www.telecinco.es/espanasinirmaslejos/Espana-gran-destino-turistico_2_1240605043.html',
        'only_matching': True,
    }, {
        # ooyala video
        'url': 'http://www.cuatro.com/chesterinlove/a-carta/chester-chester_in_love-chester_edu_2_2331030022.html',
        'only_matching': True,
    }]

    def _parse_content(self, content, url):
        video_id = content['dataMediaId']
        config = self._download_json(
            content['dataConfig'], video_id, 'Downloading config JSON')
        title = config['info']['title']
        services = config['services']
        caronte = self._download_json(services['caronte'], video_id)
        stream = caronte['dls'][0]['stream']
        headers = self.geo_verification_headers()
        headers.update({
            'Content-Type': 'application/json;charset=UTF-8',
            'Origin': re.match(r'https?://[^/]+', url).group(0),
        })
        cdn = self._download_json(
            caronte['cerbero'], video_id, data=json.dumps({
                'bbx': caronte['bbx'],
                'gbx': self._download_json(services['gbx'], video_id)['gbx'],
            }).encode(), headers=headers)['tokens']['1']['cdn']
        formats = self._extract_m3u8_formats(
            stream + '?' + cdn, video_id, 'mp4', 'm3u8_native', m3u8_id='hls')
        self._sort_formats(formats)

        return {
            'id': video_id,
            'title': title,
            'formats': formats,
            'thumbnail': content.get('dataPoster') or config.get('poster', {}).get('imageUrl'),
            'duration': int_or_none(content.get('dataDuration')),
        }

    def _real_extract(self, url):
        display_id = self._match_id(url)
        webpage = self._download_webpage(url, display_id)
        article = self._parse_json(self._search_regex(
            r'window\.\$REACTBASE_STATE\.article(?:_multisite)?\s*=\s*({.+})',
            webpage, 'article'), display_id)['article']
        title = article.get('title')
        description = clean_html(article.get('leadParagraph')) or ''
        if article.get('editorialType') != 'VID':
            entries = []
            body = [article.get('opening')]
            body.extend(try_get(article, lambda x: x['body'], list) or [])
            for p in body:
                if not isinstance(p, dict):
                    continue
                content = p.get('content')
                if not content:
                    continue
                type_ = p.get('type')
                if type_ == 'paragraph':
                    content_str = str_or_none(content)
                    if content_str:
                        description += content_str
                    continue
                if type_ == 'video' and isinstance(content, dict):
                    entries.append(self._parse_content(content, url))
            return self.playlist_result(
                entries, str_or_none(article.get('id')), title, description)
        content = article['opening']['content']
        info = self._parse_content(content, url)
        info.update({
            'description': description,
        })
        return info


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

class TelegraafIE(InfoExtractor):
    _VALID_URL = r'https?://(?:www\.)?telegraaf\.nl/video/(?P<id>\d+)'
    _TEST = {
        'url': 'https://www.telegraaf.nl/video/734366489/historisch-scheepswrak-slaat-na-100-jaar-los',
        'info_dict': {
            'id': 'gaMItuoSeUg2',
            'ext': 'mp4',
            'title': 'Historisch scheepswrak slaat na 100 jaar los',
            'description': 'md5:6f53b7c4f55596722ac24d6c0ec00cfb',
            'thumbnail': r're:^https?://.*\.jpg',
            'duration': 55,
            'timestamp': 1572805527,
            'upload_date': '20191103',
        },
        'params': {
            # m3u8 download
            'skip_download': True,
        },
    }

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

        video_id = self._download_json(
            'https://app.telegraaf.nl/graphql', article_id,
            headers={'User-Agent': 'De Telegraaf/6.8.11 (Android 11; en_US)'},
            query={
                'query': '''{
  article(uid: %s) {
    videos {
      videoId
    }
  }
}''' % article_id,
            })['data']['article']['videos'][0]['videoId']

        item = self._download_json(
            'https://content.tmgvideo.nl/playlist/item=%s/playlist.json' % video_id,
            video_id)['items'][0]
        title = item['title']

        formats = []
        locations = item.get('locations') or {}
        for location in locations.get('adaptive', []):
            manifest_url = location.get('src')
            if not manifest_url:
                continue
            ext = determine_ext(manifest_url)
            if ext == 'm3u8':
                formats.extend(self._extract_m3u8_formats(
                    manifest_url, video_id, ext='mp4', m3u8_id='hls', fatal=False))
            elif ext == 'mpd':
                formats.extend(self._extract_mpd_formats(
                    manifest_url, video_id, mpd_id='dash', fatal=False))
            else:
                self.report_warning('Unknown adaptive format %s' % ext)
        for location in locations.get('progressive', []):
            src = try_get(location, lambda x: x['sources'][0]['src'])
            if not src:
                continue
            label = location.get('label')
            formats.append({
                'url': src,
                'width': int_or_none(location.get('width')),
                'height': int_or_none(location.get('height')),
                'format_id': 'http' + ('-%s' % label if label else ''),
            })

        self._sort_formats(formats)

        return {
            'id': video_id,
            'title': title,
            'description': item.get('description'),
            'formats': formats,
            'duration': int_or_none(item.get('duration')),
            'thumbnail': item.get('poster'),
            'timestamp': parse_iso8601(item.get('datecreated'), ' '),
        }


code:
from .common import InfoExtractor
from ..utils import clean_html, get_element_by_class

class TelegramEmbedIE(InfoExtractor):
    IE_NAME = 'telegram:embed'
    _VALID_URL = r'https?://t\.me/(?P<channel_name>[^/]+)/(?P<id>\d+)'
    _TESTS = [{
        'url': 'https://t.me/europa_press/613',
        'info_dict': {
            'id': '613',
            'ext': 'mp4',
            'title': 'Europa Press',
            'description': '6ce2d7e8d56eda16d80607b23db7b252',
            'thumbnail': r're:^https?:\/\/cdn.*?telesco\.pe\/file\/\w+',
        },
    }]

    def _real_extract(self, url):
        video_id = self._match_id(url)
        webpage = self._download_webpage(url, video_id, query={'embed': 0})
        webpage_embed = self._download_webpage(url, video_id, query={'embed': 1}, note='Downloading ermbed page')

        formats = [{
            'url': self._proto_relative_url(self._search_regex(
                '<video[^>]+src="([^"]+)"', webpage_embed, 'source')),
            'ext': 'mp4',
        }]
        self._sort_formats(formats)

        return {
            'id': video_id,
            'title': self._html_search_meta(['og:title', 'twitter:title'], webpage, default=None),
            'description': self._html_search_meta(
                ['og:description', 'twitter:description'], webpage,
                default=clean_html(get_element_by_class('tgme_widget_message_text', webpage_embed))),
            'thumbnail': self._search_regex(
                r'tgme_widget_message_video_thumb"[^>]+background-image:url\(\'([^\']+)\'\)',
                webpage_embed, 'thumbnail'),
            'formats': formats,
        }


code:
import re

from .common import InfoExtractor
from ..utils import remove_start

class TeleMBIE(InfoExtractor):
    _VALID_URL = r'https?://(?:www\.)?telemb\.be/(?P<display_id>.+?)_d_(?P<id>\d+)\.html'
    _TESTS = [
        {
            'url': 'http://www.telemb.be/mons-cook-with-danielle-des-cours-de-cuisine-en-anglais-_d_13466.html',
            'md5': 'f45ea69878516ba039835794e0f8f783',
            'info_dict': {
                'id': '13466',
                'display_id': 'mons-cook-with-danielle-des-cours-de-cuisine-en-anglais-',
                'ext': 'mp4',
                'title': 'Mons - Cook with Danielle : des cours de cuisine en anglais ! - Les reportages',
                'description': 'md5:bc5225f47b17c309761c856ad4776265',
                'thumbnail': r're:^http://.*\.(?:jpg|png)$',
            }
        },
        {
            # non-ASCII characters in download URL
            'url': 'http://telemb.be/les-reportages-havre-incendie-mortel_d_13514.html',
            'md5': '6e9682736e5ccd4eab7f21e855350733',
            'info_dict': {
                'id': '13514',
                'display_id': 'les-reportages-havre-incendie-mortel',
                'ext': 'mp4',
                'title': 'Havré - Incendie mortel - Les reportages',
                'description': 'md5:5e54cb449acb029c2b7734e2d946bd4a',
                'thumbnail': r're:^http://.*\.(?:jpg|png)$',
            }
        },
    ]

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

        webpage = self._download_webpage(url, display_id)

        formats = []
        for video_url in re.findall(r'file\s*:\s*"([^"]+)"', webpage):
            fmt = {
                'url': video_url,
                'format_id': video_url.split(':')[0]
            }
            rtmp = re.search(r'^(?P<url>rtmp://[^/]+/(?P<app>.+))/(?P<playpath>mp4:.+)$', video_url)
            if rtmp:
                fmt.update({
                    'play_path': rtmp.group('playpath'),
                    'app': rtmp.group('app'),
                    'player_url': 'http://p.jwpcdn.com/6/10/jwplayer.flash.swf',
                    'page_url': 'http://www.telemb.be',
                    'preference': -10,
                })
            formats.append(fmt)
        self._sort_formats(formats)

        title = remove_start(self._og_search_title(webpage), 'TéléMB : ')
        description = self._html_search_regex(
            r'<meta property="og:description" content="(.+?)" />',
            webpage, 'description', fatal=False)
        thumbnail = self._og_search_thumbnail(webpage)

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


code:
from .common import InfoExtractor
from ..utils import (
    try_get,
    unified_timestamp,
    HEADRequest,
)

class TelemundoIE(InfoExtractor):

    _VALID_URL = r'https?:\/\/(?:www\.)?telemundo\.com\/.+?video\/[^\/]+(?P<id>tmvo\d{7})'
    _TESTS = [{
        'url': 'https://www.telemundo.com/noticias/noticias-telemundo-en-la-noche/empleo/video/esta-aplicacion-gratuita-esta-ayudando-los-latinos-encontrar-trabajo-en-estados-unidos-tmvo9829325',
        'info_dict': {
            'id': 'tmvo9829325',
            'timestamp': 1621396800,
            'title': 'Esta aplicación gratuita está ayudando a los latinos a encontrar trabajo en Estados Unidos',
            'uploader': 'Telemundo',
            'uploader_id': 'NBCU_Telemundo',
            'ext': 'mp4',
            'upload_date': '20210519',
        },
        'params': {
            'skip_download': True,
        }
    }, {
        'url': 'https://www.telemundo.com/shows/al-rojo-vivo/empleo/video/personajes-de-times-square-piden-que-la-ciudad-de-nueva-york-los-deje-volver-trabajar-tmvo9816272',
        'only_matching': True,
    }]

    def _real_extract(self, url):
        video_id = self._match_id(url)
        webpage = self._download_webpage(url, video_id)
        metadata = self._search_nextjs_data(webpage, video_id)
        redirect_url = try_get(
            metadata,
            lambda x: x['props']['initialState']['video']['associatedPlaylists'][0]['videos'][0]['videoAssets'][0]['publicUrl'])

        m3u8_url = self._request_webpage(HEADRequest(
            redirect_url + '?format=redirect&manifest=m3u&format=redirect&Tracking=true&Embedded=true&formats=MPEG4'),
            video_id, 'Processing m3u8').geturl()
        formats = self._extract_m3u8_formats(m3u8_url, video_id, 'mp4')
        self._sort_formats(formats)
        date = unified_timestamp(try_get(
            metadata, lambda x: x['props']['initialState']['video']['associatedPlaylists'][0]['videos'][0]['datePublished'].split(' ', 1)[1]))
        return {
            'url': url,
            'id': video_id,
            'title': self._search_regex(r'<h1[^>]+>([^<]+)', webpage, 'title', fatal=False),
            'formats': formats,
            'timestamp': date,
            'uploader': 'Telemundo',
            'uploader_id': self._search_regex(r'https?:\/\/(?:[^/]+\/){3}video\/(?P<id>[^\/]+)', m3u8_url, 'Akamai account', fatal=False)
        }


code:
from .common import InfoExtractor
from ..compat import compat_str
from ..utils import (
    int_or_none,
    smuggle_url,
    try_get,
    unified_timestamp,
)

class TeleQuebecBaseIE(InfoExtractor):
    BRIGHTCOVE_URL_TEMPLATE = 'http://players.brightcove.net/%s/%s_default/index.html?videoId=%s'

    @staticmethod
    def _brightcove_result(brightcove_id, player_id, account_id='6150020952001'):
        return {
            '_type': 'url_transparent',
            'url': smuggle_url(TeleQuebecBaseIE.BRIGHTCOVE_URL_TEMPLATE % (account_id, player_id, brightcove_id), {'geo_countries': ['CA']}),
            'ie_key': 'BrightcoveNew',
        }

class TeleQuebecIE(TeleQuebecBaseIE):
    _VALID_URL = r'''(?x)
                    https?://
                        (?:
                            zonevideo\.telequebec\.tv/media|
                            coucou\.telequebec\.tv/videos
                        )/(?P<id>\d+)
                    '''
    _TESTS = [{
        # available till 01.01.2023
        'url': 'http://zonevideo.telequebec.tv/media/37578/un-petit-choc-et-puis-repart/un-chef-a-la-cabane',
        'info_dict': {
            'id': '6155972771001',
            'ext': 'mp4',
            'title': 'Un petit choc et puis repart!',
            'description': 'md5:b04a7e6b3f74e32d7b294cffe8658374',
            'timestamp': 1589262469,
            'uploader_id': '6150020952001',
            'upload_date': '20200512',
        },
        'add_ie': ['BrightcoveNew'],
    }, {
        'url': 'https://zonevideo.telequebec.tv/media/55267/le-soleil/passe-partout',
        'info_dict': {
            'id': '6167180337001',
            'ext': 'mp4',
            'title': 'Le soleil',
            'description': 'md5:64289c922a8de2abbe99c354daffde02',
            'uploader_id': '6150020952001',
            'upload_date': '20200625',
            'timestamp': 1593090307,
        },
        'add_ie': ['BrightcoveNew'],
    }, {
        # no description
        'url': 'http://zonevideo.telequebec.tv/media/30261',
        'only_matching': True,
    }, {
        'url': 'https://coucou.telequebec.tv/videos/41788/idee-de-genie/l-heure-du-bain',
        'only_matching': True,
    }]

    def _real_extract(self, url):
        media_id = self._match_id(url)
        media = self._download_json(
            'https://mnmedias.api.telequebec.tv/api/v3/media/' + media_id,
            media_id)['media']
        source_id = next(source_info['sourceId'] for source_info in media['streamInfos'] if source_info.get('source') == 'Brightcove')
        info = self._brightcove_result(source_id, '22gPKdt7f')
        product = media.get('product') or {}
        season = product.get('season') or {}
        info.update({
            'description': try_get(media, lambda x: x['descriptions'][-1]['text'], compat_str),
            'series': try_get(season, lambda x: x['serie']['titre']),
            'season': season.get('name'),
            'season_number': int_or_none(season.get('seasonNo')),
            'episode': product.get('titre'),
            'episode_number': int_or_none(product.get('episodeNo')),
        })
        return info

class TeleQuebecSquatIE(InfoExtractor):
    _VALID_URL = r'https://squat\.telequebec\.tv/videos/(?P<id>\d+)'
    _TESTS = [{
        'url': 'https://squat.telequebec.tv/videos/9314',
        'info_dict': {
            'id': 'd59ae78112d542e793d83cc9d3a5b530',
            'ext': 'mp4',
            'title': 'Poupeflekta',
            'description': 'md5:2f0718f8d2f8fece1646ee25fb7bce75',
            'duration': 1351,
            'timestamp': 1569057600,
            'upload_date': '20190921',
            'series': 'Miraculous : Les Aventures de Ladybug et Chat Noir',
            'season': 'Saison 3',
            'season_number': 3,
            'episode_number': 57,
        },
        'params': {
            'skip_download': True,
        },
    }]

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

        video = self._download_json(
            'https://squat.api.telequebec.tv/v1/videos/%s' % video_id,
            video_id)

        media_id = video['sourceId']

        return {
            '_type': 'url_transparent',
            'url': 'http://zonevideo.telequebec.tv/media/%s' % media_id,
            'ie_key': TeleQuebecIE.ie_key(),
            'id': media_id,
            'title': video.get('titre'),
            'description': video.get('description'),
            'timestamp': unified_timestamp(video.get('datePublication')),
            'series': video.get('container'),
            'season': video.get('saison'),
            'season_number': int_or_none(video.get('noSaison')),
            'episode_number': int_or_none(video.get('episode')),
        }

class TeleQuebecEmissionIE(InfoExtractor):
    _VALID_URL = r'''(?x)
                    https?://
                        (?:
                            [^/]+\.telequebec\.tv/emissions/|
                            (?:www\.)?telequebec\.tv/
                        )
                        (?P<id>[^?#&]+)
                    '''
    _TESTS = [{
        'url': 'http://lindicemcsween.telequebec.tv/emissions/100430013/des-soins-esthetiques-a-377-d-interets-annuels-ca-vous-tente',
        'info_dict': {
            'id': '6154476028001',
            'ext': 'mp4',
            'title': 'Des soins esthétiques à 377 % d’intérêts annuels, ça vous tente?',
            'description': 'md5:cb4d378e073fae6cce1f87c00f84ae9f',
            'upload_date': '20200505',
            'timestamp': 1588713424,
            'uploader_id': '6150020952001',
        },
    }, {
        'url': 'http://bancpublic.telequebec.tv/emissions/emission-49/31986/jeunes-meres-sous-pression',
        'only_matching': True,
    }, {
        'url': 'http://www.telequebec.tv/masha-et-michka/epi059masha-et-michka-3-053-078',
        'only_matching': True,
    }, {
        'url': 'http://www.telequebec.tv/documentaire/bebes-sur-mesure/',
        'only_matching': True,
    }]

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

        webpage = self._download_webpage(url, display_id)

        media_id = self._search_regex(
            r'mediaId\s*:\s*(?P<id>\d+)', webpage, 'media id')

        return self.url_result(
            'http://zonevideo.telequebec.tv/media/' + media_id,
            TeleQuebecIE.ie_key())

class TeleQuebecLiveIE(TeleQuebecBaseIE):
    _VALID_URL = r'https?://zonevideo\.telequebec\.tv/(?P<id>endirect)'
    _TEST = {
        'url': 'http://zonevideo.telequebec.tv/endirect/',
        'info_dict': {
            'id': '6159095684001',
            'ext': 'mp4',
            'title': 're:^Télé-Québec [0-9]{4}-[0-9]{2}-[0-9]{2} [0-9]{2}:[0-9]{2}$',
            'is_live': True,
            'description': 'Canal principal de Télé-Québec',
            'uploader_id': '6150020952001',
            'timestamp': 1590439901,
            'upload_date': '20200525',
        },
        'params': {
            'skip_download': True,
        },
    }

    def _real_extract(self, url):
        return self._brightcove_result('6159095684001', 'skCsmi2Uw')

class TeleQuebecVideoIE(TeleQuebecBaseIE):
    _VALID_URL = r'https?://video\.telequebec\.tv/player(?:-live)?/(?P<id>\d+)'
    _TESTS = [{
        'url': 'https://video.telequebec.tv/player/31110/stream',
        'info_dict': {
            'id': '6202570652001',
            'ext': 'mp4',
            'title': 'Le coût du véhicule le plus vendu au Canada / Tous les frais liés à la procréation assistée',
            'description': 'md5:685a7e4c450ba777c60adb6e71e41526',
            'upload_date': '20201019',
            'timestamp': 1603115930,
            'uploader_id': '6101674910001',
        },
    }, {
        'url': 'https://video.telequebec.tv/player-live/28527',
        'only_matching': True,
    }]

    def _call_api(self, path, video_id):
        return self._download_json(
            'http://beacon.playback.api.brightcove.com/telequebec/api/assets/' + path,
            video_id, query={'device_layout': 'web', 'device_type': 'web'})['data']

    def _real_extract(self, url):
        asset_id = self._match_id(url)
        asset = self._call_api(asset_id, asset_id)['asset']
        stream = self._call_api(
            asset_id + '/streams/' + asset['streams'][0]['id'], asset_id)['stream']
        stream_url = stream['url']
        account_id = try_get(
            stream, lambda x: x['video_provider_details']['account_id']) or '6101674910001'
        info = self._brightcove_result(stream_url, 'default', account_id)
        info.update({
            'description': asset.get('long_description') or asset.get('short_description'),
            'series': asset.get('series_original_name'),
            'season_number': int_or_none(asset.get('season_number')),
            'episode': asset.get('original_name'),
            'episode_number': int_or_none(asset.get('episode_number')),
        })
        return info


code:
import re

from .common import InfoExtractor
from ..utils import unified_strdate

class TeleTaskIE(InfoExtractor):
    _VALID_URL = r'https?://(?:www\.)?tele-task\.de/archive/video/html5/(?P<id>[0-9]+)'
    _TEST = {
        'url': 'http://www.tele-task.de/archive/video/html5/26168/',
        'info_dict': {
            'id': '26168',
            'title': 'Duplicate Detection',
        },
        'playlist': [{
            'md5': '290ef69fb2792e481169c3958dbfbd57',
            'info_dict': {
                'id': '26168-speaker',
                'ext': 'mp4',
                'title': 'Duplicate Detection',
                'upload_date': '20141218',
            }
        }, {
            'md5': 'e1e7218c5f0e4790015a437fcf6c71b4',
            'info_dict': {
                'id': '26168-slides',
                'ext': 'mp4',
                'title': 'Duplicate Detection',
                'upload_date': '20141218',
            }
        }]
    }

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

        title = self._html_search_regex(
            r'itemprop="name">([^<]+)', webpage, 'title')
        upload_date = unified_strdate(self._html_search_regex(
            r'Date:	([^<]+)	', webpage, 'date', fatal=False))

        entries = [{
            'id': '%s-%s' % (lecture_id, format_id),
            'url': video_url,
            'title': title,
            'upload_date': upload_date,
        } for format_id, video_url in re.findall(
            r'<video class="([^"]+)"[^>]*>\s*<source src="([^"]+)"', webpage)]

        return self.playlist_result(entries, lecture_id, title)


code:
from .common import InfoExtractor

class TelewebionIE(InfoExtractor):
    _VALID_URL = r'https?://(?:www\.)?telewebion\.com/#!/episode/(?P<id>\d+)'

    _TEST = {
        'url': 'http://www.telewebion.com/#!/episode/1263668/',
        'info_dict': {
            'id': '1263668',
            'ext': 'mp4',
            'title': 'قرعه\u200cکشی لیگ قهرمانان اروپا',
            'thumbnail': r're:^https?://.*\.jpg',
            'view_count': int,
        },
        'params': {
            # m3u8 download
            'skip_download': True,
        },
    }

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

        secure_token = self._download_webpage(
            'http://m.s2.telewebion.com/op/op?action=getSecurityToken', video_id)
        episode_details = self._download_json(
            'http://m.s2.telewebion.com/op/op', video_id,
            query={'action': 'getEpisodeDetails', 'episode_id': video_id})

        m3u8_url = 'http://m.s1.telewebion.com/smil/%s.m3u8?filepath=%s&m3u8=1&secure_token=%s' % (
            video_id, episode_details['file_path'], secure_token)
        formats = self._extract_m3u8_formats(
            m3u8_url, video_id, ext='mp4', m3u8_id='hls')

        picture_paths = [
            episode_details.get('picture_path'),
            episode_details.get('large_picture_path'),
        ]

        thumbnails = [{
            'url': picture_path,
            'preference': idx,
        } for idx, picture_path in enumerate(picture_paths) if picture_path is not None]

        return {
            'id': video_id,
            'title': episode_details['title'],
            'formats': formats,
            'thumbnails': thumbnails,
            'view_count': episode_details.get('view_count'),
        }


code:
from .common import InfoExtractor
from ..utils import int_or_none, parse_iso8601, str_or_none, traverse_obj

class TempoIE(InfoExtractor):
    _VALID_URL = r'https?://video\.tempo\.co/\w+/\d+/(?P<id>[\w-]+)'
    _TESTS = [{
        'url': 'https://video.tempo.co/read/30058/anies-baswedan-ajukan-banding-putusan-ptun-batalkan-ump-dki',
        'info_dict': {
            'id': '2144438',
            'ext': 'mp4',
            'title': 'Anies Baswedan Ajukan Banding Putusan PTUN Batalkan UMP DKI',
            'display_id': 'anies-baswedan-ajukan-banding-putusan-ptun-batalkan-ump-dki',
            'duration': 84,
            'description': 'md5:a6822b7c4c874fa7e5bd63e96a387b66',
            'thumbnail': 'https://statik.tempo.co/data/2022/07/27/id_1128287/1128287_720.jpg',
            'timestamp': 1658911277,
            'upload_date': '20220727',
            'tags': ['Anies Baswedan', ' PTUN', ' PTUN | Pengadilan Tata Usaha Negara', ' PTUN Batalkan UMP DKI', ' UMP DKI'],
        }
    }]

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

        player_key, widget_id = self._search_regex(
            r'<ivs-player\s*[^>]+data-ivs-key\s*=\s*"(?P<player_key>[\w]+)[^>]+\bdata-ivs-wid="(?P<widget_id>[\w-]+)',
            webpage, 'player_key, widget_id', group=('player_key', 'widget_id'))

        json_ld_data = self._search_json_ld(webpage, display_id)

        json_data = self._download_json(
            f'https://ivxplayer.ivideosmart.com/prod/widget/{widget_id}',
            display_id, query={'key': player_key})
        formats, subtitles = self._extract_m3u8_formats_and_subtitles(
            json_data['player']['video_url'], display_id, ext='mp4')

        return {
            'id': str(json_data['ivx']['id']),
            'display_id': display_id,
            'formats': formats,
            'subtitles': subtitles,
            'title': (self._html_search_meta('twitter:title', webpage) or self._og_search_title(webpage)
                      or traverse_obj(json_data, ('ivx', 'name'))),
            'duration': int_or_none(traverse_obj(json_data, ('ivx', 'duration'))),
            'thumbnail': (self._html_search_meta('twitter:image:src', webpage) or self._og_search_thumbnail(webpage)
                          or traverse_obj(json_data, ('ivx', 'thumbnail_url'))),
            'description': (json_ld_data.get('description') or self._html_search_meta(['description', 'twitter:description'], webpage)
                            or self._og_search_description(webpage)),
            'timestamp': parse_iso8601(traverse_obj(json_data, ('ivx', 'created_at'))),
            'tags': str_or_none(self._html_search_meta('keywords', webpage), '').split(','),
        }