code:
import functools

from .common import InfoExtractor
from ..utils import (
    OnDemandPagedList,
    traverse_obj,
    unified_strdate,
)

class GronkhIE(InfoExtractor):
    _VALID_URL = r'https?://(?:www\.)?gronkh\.tv/(?:watch/)?stream/(?P<id>\d+)'

    _TESTS = [{
        'url': 'https://gronkh.tv/stream/536',
        'info_dict': {
            'id': '536',
            'ext': 'mp4',
            'title': 'GTV0536, 2021-10-01 - MARTHA IS DEAD  #FREiAB1830  !FF7 !horde !archiv',
            'view_count': 19491,
            'thumbnail': 'https://01.cdn.vod.farm/preview/6436746cce14e25f751260a692872b9b.jpg',
            'upload_date': '20211001'
        },
        'params': {'skip_download': True}
    }, {
        'url': 'https://gronkh.tv/watch/stream/546',
        'only_matching': True,
    }]

    def _real_extract(self, url):
        id = self._match_id(url)
        data_json = self._download_json(f'https://api.gronkh.tv/v1/video/info?episode={id}', id)
        m3u8_url = self._download_json(f'https://api.gronkh.tv/v1/video/playlist?episode={id}', id)['playlist_url']
        formats, subtitles = self._extract_m3u8_formats_and_subtitles(m3u8_url, id)
        if data_json.get('vtt_url'):
            subtitles.setdefault('en', []).append({
                'url': data_json['vtt_url'],
                'ext': 'vtt',
            })
        self._sort_formats(formats)
        return {
            'id': id,
            'title': data_json.get('title'),
            'view_count': data_json.get('views'),
            'thumbnail': data_json.get('preview_url'),
            'upload_date': unified_strdate(data_json.get('created_at')),
            'formats': formats,
            'subtitles': subtitles,
        }

class GronkhFeedIE(InfoExtractor):
    _VALID_URL = r'https?://(?:www\.)?gronkh\.tv(?:/feed)?/?(?:#|$)'
    IE_NAME = 'gronkh:feed'

    _TESTS = [{
        'url': 'https://gronkh.tv/feed',
        'info_dict': {
            'id': 'feed',
        },
        'playlist_count': 16,
    }, {
        'url': 'https://gronkh.tv',
        'only_matching': True,
    }]

    def _entries(self):
        for type_ in ('recent', 'views'):
            info = self._download_json(
                f'https://api.gronkh.tv/v1/video/discovery/{type_}', 'feed', note=f'Downloading {type_} API JSON')
            for item in traverse_obj(info, ('discovery', ...)) or []:
                yield self.url_result(f'https://gronkh.tv/watch/stream/{item["episode"]}', GronkhIE, item.get('title'))

    def _real_extract(self, url):
        return self.playlist_result(self._entries(), 'feed')

class GronkhVodsIE(InfoExtractor):
    _VALID_URL = r'https?://(?:www\.)?gronkh\.tv/vods/streams/?(?:#|$)'
    IE_NAME = 'gronkh:vods'

    _TESTS = [{
        'url': 'https://gronkh.tv/vods/streams',
        'info_dict': {
            'id': 'vods',
        },
        'playlist_mincount': 150,
    }]
    _PER_PAGE = 25

    def _fetch_page(self, page):
        items = traverse_obj(self._download_json(
            'https://api.gronkh.tv/v1/search', 'vods', query={'offset': self._PER_PAGE * page, 'first': self._PER_PAGE},
            note=f'Downloading stream video page {page + 1}'), ('results', 'videos', ...))
        for item in items or []:
            yield self.url_result(f'https://gronkh.tv/watch/stream/{item["episode"]}', GronkhIE, item['episode'], item.get('title'))

    def _real_extract(self, url):
        entries = OnDemandPagedList(functools.partial(self._fetch_page), self._PER_PAGE)
        return self.playlist_result(entries, 'vods')


code:
from .common import InfoExtractor

class GrouponIE(InfoExtractor):
    _VALID_URL = r'https?://(?:www\.)?groupon\.com/deals/(?P<id>[^/?#&]+)'

    _TEST = {
        'url': 'https://www.groupon.com/deals/bikram-yoga-huntington-beach-2#ooid=tubGNycTo_9Uxg82uESj4i61EYX8nyuf',
        'info_dict': {
            'id': 'bikram-yoga-huntington-beach-2',
            'title': '$49 for 10 Yoga Classes or One Month of Unlimited Classes at Bikram Yoga Huntington Beach ($180 Value)',
            'description': 'Studio kept at 105 degrees and 40% humidity with anti-microbial and anti-slip Flotex flooring; certified instructors',
        },
        'playlist': [{
            'md5': '42428ce8a00585f9bc36e49226eae7a1',
            'info_dict': {
                'id': 'fk6OhWpXgIQ',
                'ext': 'mp4',
                'title': 'Bikram Yoga Huntington Beach | Orange County !tubGNycTo@9Uxg82uESj4i61EYX8nyuf',
                'description': 'md5:d41d8cd98f00b204e9800998ecf8427e',
                'duration': 45,
                'upload_date': '20160405',
                'uploader_id': 'groupon',
                'uploader': 'Groupon',
            },
            'add_ie': ['Youtube'],
        }],
        'params': {
            'skip_download': True,
        },
    }

    _PROVIDERS = {
        'ooyala': ('ooyala:%s', 'Ooyala'),
        'youtube': ('%s', 'Youtube'),
    }

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

        payload = self._parse_json(self._search_regex(
            r'(?:var\s+|window\.)payload\s*=\s*(.*?);\n', webpage, 'payload'), playlist_id)
        videos = payload['carousel'].get('dealVideos', [])
        entries = []
        for v in videos:
            provider = v.get('provider')
            video_id = v.get('media') or v.get('id') or v.get('baseURL')
            if not provider or not video_id:
                continue
            url_pattern, ie_key = self._PROVIDERS.get(provider.lower())
            if not url_pattern:
                self.report_warning(
                    '%s: Unsupported video provider %s, skipping video' %
                    (playlist_id, provider))
                continue
            entries.append(self.url_result(url_pattern % video_id, ie_key))

        return {
            '_type': 'playlist',
            'id': playlist_id,
            'entries': entries,
            'title': self._og_search_title(webpage),
            'description': self._og_search_description(webpage),
        }

repo_name: Lesmiscore/ytdl


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

class HarpodeonIE(InfoExtractor):
    _VALID_URL = r'https?://(?:www\.)?harpodeon\.com/(?:video|preview)/\w+/(?P<id>\d+)'
    _TESTS = [{
        'url': 'https://www.harpodeon.com/video/The_Smoking_Out_of_Bella_Butts/268068288',
        'md5': '727371564a6a9ebccef2073535b5b6bd',
        'skip': 'Free video could become unavailable',
        'info_dict': {
            'id': '268068288',
            'ext': 'mp4',
            'title': 'The Smoking Out of Bella Butts',
            'description': 'md5:47e16bdb41fc8a79c83ab83af11c8b77',
            'creator': 'Vitagraph Company of America',
            'release_date': '19150101'
        }
    }, {
        'url': 'https://www.harpodeon.com/preview/The_Smoking_Out_of_Bella_Butts/268068288',
        'md5': '6dfea5412845f690c7331be703f884db',
        'info_dict': {
            'id': '268068288',
            'ext': 'mp4',
            'title': 'The Smoking Out of Bella Butts',
            'description': 'md5:47e16bdb41fc8a79c83ab83af11c8b77',
            'creator': 'Vitagraph Company of America',
            'release_date': '19150101'
        }
    }, {
        'url': 'https://www.harpodeon.com/preview/Behind_the_Screen/421838710',
        'md5': '7979df9ca04637282cb7d172ab3a9c3b',
        'info_dict': {
            'id': '421838710',
            'ext': 'mp4',
            'title': 'Behind the Screen',
            'description': 'md5:008972a3dc51fba3965ee517d2ba9155',
            'creator': 'Lone Star Corporation',
            'release_date': '19160101'
        }
    }]

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

        title, creator, release_year = self._search_regex(
            r'''(?x)
                <div[^>]+videoInfo[^<]*<h2[^>]*>(?P<title>[^>]+)</h2>
                (?:\s*<p[^>]*>\((?P<creator>.+),\s*)?(?P<release_year>\d{4})?''',
            webpage, 'title', group=('title', 'creator', 'release_year'),
            fatal=False) or (None, None, None)

        hp_base = self._html_search_regex(r'hpBase\(\s*["\']([^"\']+)', webpage, 'hp_base')

        hp_inject_video, hp_resolution = self._search_regex(
            r'''(?x)
                hpInjectVideo\([\'\"](?P<hp_inject_video>\w+)[\'\"],
                [\'\"](?P<hp_resolution>\d+)[\'\"]''',
            webpage, 'hp_inject_video', group=['hp_inject_video', 'hp_resolution'])

        return {
            'id': video_id,
            'title': title,
            'url': f'{hp_base}{hp_inject_video}_{hp_resolution}.mp4',
            'http_headers': {'Referer': url},
            'description': self._html_search_meta('description', webpage, fatal=False),
            'creator': creator,
            'release_date': unified_strdate(f'{release_year}0101')
        }


code:
import re

from .common import InfoExtractor
from ..utils import (
    xpath_text,
    xpath_element,
    int_or_none,
    parse_duration,
    urljoin,
)

class HBOBaseIE(InfoExtractor):
    _FORMATS_INFO = {
        'pro7': {
            'width': 1280,
            'height': 720,
        },
        '1920': {
            'width': 1280,
            'height': 720,
        },
        'pro6': {
            'width': 768,
            'height': 432,
        },
        '640': {
            'width': 768,
            'height': 432,
        },
        'pro5': {
            'width': 640,
            'height': 360,
        },
        'highwifi': {
            'width': 640,
            'height': 360,
        },
        'high3g': {
            'width': 640,
            'height': 360,
        },
        'medwifi': {
            'width': 400,
            'height': 224,
        },
        'med3g': {
            'width': 400,
            'height': 224,
        },
    }

    def _extract_info(self, url, display_id):
        video_data = self._download_xml(url, display_id)
        video_id = xpath_text(video_data, 'id', fatal=True)
        episode_title = title = xpath_text(video_data, 'title', fatal=True)
        series = xpath_text(video_data, 'program')
        if series:
            title = '%s - %s' % (series, title)

        formats = []
        for source in xpath_element(video_data, 'videos', 'sources', True):
            if source.tag == 'size':
                path = xpath_text(source, './/path')
                if not path:
                    continue
                width = source.attrib.get('width')
                format_info = self._FORMATS_INFO.get(width, {})
                height = format_info.get('height')
                fmt = {
                    'url': path,
                    'format_id': 'http%s' % ('-%dp' % height if height else ''),
                    'width': format_info.get('width'),
                    'height': height,
                }
                rtmp = re.search(r'^(?P<url>rtmpe?://[^/]+/(?P<app>.+))/(?P<playpath>mp4:.+)$', path)
                if rtmp:
                    fmt.update({
                        'url': rtmp.group('url'),
                        'play_path': rtmp.group('playpath'),
                        'app': rtmp.group('app'),
                        'ext': 'flv',
                        'format_id': fmt['format_id'].replace('http', 'rtmp'),
                    })
                formats.append(fmt)
            else:
                video_url = source.text
                if not video_url:
                    continue
                if source.tag == 'tarball':
                    formats.extend(self._extract_m3u8_formats(
                        video_url.replace('.tar', '/base_index_w8.m3u8'),
                        video_id, 'mp4', 'm3u8_native', m3u8_id='hls', fatal=False))
                elif source.tag == 'hls':
                    m3u8_formats = self._extract_m3u8_formats(
                        video_url.replace('.tar', '/base_index.m3u8'),
                        video_id, 'mp4', 'm3u8_native', m3u8_id='hls', fatal=False)
                    for f in m3u8_formats:
                        if f.get('vcodec') == 'none' and not f.get('tbr'):
                            f['tbr'] = int_or_none(self._search_regex(
                                r'-(\d+)k/', f['url'], 'tbr', default=None))
                    formats.extend(m3u8_formats)
                elif source.tag == 'dash':
                    formats.extend(self._extract_mpd_formats(
                        video_url.replace('.tar', '/manifest.mpd'),
                        video_id, mpd_id='dash', fatal=False))
                else:
                    format_info = self._FORMATS_INFO.get(source.tag, {})
                    formats.append({
                        'format_id': 'http-%s' % source.tag,
                        'url': video_url,
                        'width': format_info.get('width'),
                        'height': format_info.get('height'),
                    })
        self._sort_formats(formats)

        thumbnails = []
        card_sizes = xpath_element(video_data, 'titleCardSizes')
        if card_sizes is not None:
            for size in card_sizes:
                path = xpath_text(size, 'path')
                if not path:
                    continue
                width = int_or_none(size.get('width'))
                thumbnails.append({
                    'id': width,
                    'url': path,
                    'width': width,
                })

        subtitles = None
        caption_url = xpath_text(video_data, 'captionUrl')
        if caption_url:
            subtitles = {
                'en': [{
                    'url': caption_url,
                    'ext': 'ttml'
                }],
            }

        return {
            'id': video_id,
            'title': title,
            'duration': parse_duration(xpath_text(video_data, 'duration/tv14')),
            'series': series,
            'episode': episode_title,
            'formats': formats,
            'thumbnails': thumbnails,
            'subtitles': subtitles,
        }

class HBOIE(HBOBaseIE):
    IE_NAME = 'hbo'
    _VALID_URL = r'https?://(?:www\.)?hbo\.com/(?:video|embed)(?:/[^/]+)*/(?P<id>[^/?#]+)'
    _TEST = {
        'url': 'https://www.hbo.com/video/game-of-thrones/seasons/season-8/videos/trailer',
        'md5': '8126210656f433c452a21367f9ad85b3',
        'info_dict': {
            'id': '22113301',
            'ext': 'mp4',
            'title': 'Game of Thrones - Trailer',
        },
        'expected_warnings': ['Unknown MIME type application/mp4 in DASH manifest'],
    }

    def _real_extract(self, url):
        display_id = self._match_id(url)
        webpage = self._download_webpage(url, display_id)
        location_path = self._parse_json(self._html_search_regex(
            r'data-state="({.+?})"', webpage, 'state'), display_id)['video']['locationUrl']
        return self._extract_info(urljoin(url, location_path), display_id)


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

class HearThisAtIE(InfoExtractor):
    _VALID_URL = r'https?://(?:www\.)?hearthis\.at/(?P<artist>[^/]+)/(?P<title>[A-Za-z0-9\-]+)/?$'
    _PLAYLIST_URL = 'https://hearthis.at/playlist.php'
    _TESTS = [{
        'url': 'https://hearthis.at/moofi/dr-kreep',
        'md5': 'ab6ec33c8fed6556029337c7885eb4e0',
        'info_dict': {
            'id': '150939',
            'ext': 'wav',
            'title': 'Moofi - Dr. Kreep',
            'thumbnail': r're:^https?://.*\.jpg$',
            'timestamp': 1421564134,
            'description': 'md5:1adb0667b01499f9d27e97ddfd53852a',
            'upload_date': '20150118',
            'view_count': int,
            'duration': 71,
            'genre': 'Experimental',
        }
    }, {
        # 'download' link redirects to the original webpage
        'url': 'https://hearthis.at/twitchsf/dj-jim-hopkins-totally-bitchin-80s-dance-mix/',
        'md5': '5980ceb7c461605d30f1f039df160c6e',
        'info_dict': {
            'id': '811296',
            'ext': 'mp3',
            'title': 'TwitchSF - DJ Jim Hopkins -  Totally Bitchin\' 80\'s Dance Mix!',
            'description': 'md5:ef26815ca8f483272a87b137ff175be2',
            'upload_date': '20160328',
            'timestamp': 1459186146,
            'thumbnail': r're:^https?://.*\.jpg$',
            'view_count': int,
            'duration': 4360,
            'genre': 'Dance',
        },
    }]

    def _real_extract(self, url):
        m = self._match_valid_url(url)
        display_id = '{artist:s} - {title:s}'.format(**m.groupdict())
        api_url = url.replace('www.', '').replace('hearthis.at', 'api-v2.hearthis.at')
        data_json = self._download_json(api_url, display_id)
        track_id = data_json.get('id')
        artist_json = data_json.get('user')
        title = '{} - {}'.format(artist_json.get('username'), data_json.get('title'))
        genre = data_json.get('genre')
        description = data_json.get('description')
        thumbnail = data_json.get('artwork_url') or data_json.get('thumb')
        view_count = str_to_int(data_json.get('playback_count'))
        duration = str_to_int(data_json.get('duration'))
        timestamp = data_json.get('release_timestamp')

        formats = []
        mp3_url = data_json.get('stream_url')

        if mp3_url:
            formats.append({
                'format_id': 'mp3',
                'vcodec': 'none',
                'acodec': 'mp3',
                'url': mp3_url,
                'ext': 'mp3',
            })

        if data_json.get('download_url'):
            download_url = data_json['download_url']
            ext = determine_ext(data_json['download_filename'])
            if ext in KNOWN_EXTENSIONS:
                formats.append({
                    'format_id': ext,
                    'vcodec': 'none',
                    'ext': ext,
                    'url': download_url,
                    'acodec': ext,
                    'quality': 2,  # Usually better quality
                })
        self._sort_formats(formats)

        return {
            'id': track_id,
            'display_id': display_id,
            'title': title,
            'formats': formats,
            'thumbnail': thumbnail,
            'description': description,
            'duration': duration,
            'timestamp': timestamp,
            'view_count': view_count,
            'genre': genre,
        }


code:
from .common import InfoExtractor
from .kaltura import KalturaIE
from .youtube import YoutubeIE
from ..utils import (
    determine_ext,
    int_or_none,
    NO_DEFAULT,
    parse_iso8601,
    smuggle_url,
    xpath_text,
)

class HeiseIE(InfoExtractor):
    _VALID_URL = r'https?://(?:www\.)?heise\.de/(?:[^/]+/)+[^/]+-(?P<id>[0-9]+)\.html'
    _TESTS = [{
        # kaltura embed
        'url': 'http://www.heise.de/video/artikel/Podcast-c-t-uplink-3-3-Owncloud-Tastaturen-Peilsender-Smartphone-2404147.html',
        'info_dict': {
            'id': '1_kkrq94sm',
            'ext': 'mp4',
            'title': "Podcast: c't uplink 3.3 – Owncloud / Tastaturen / Peilsender Smartphone",
            'timestamp': 1512734959,
            'upload_date': '20171208',
            'description': 'md5:c934cbfb326c669c2bcabcbe3d3fcd20',
        },
        'params': {
            'skip_download': True,
        },
    }, {
        # YouTube embed
        'url': 'http://www.heise.de/newsticker/meldung/Netflix-In-20-Jahren-vom-Videoverleih-zum-TV-Revolutionaer-3814130.html',
        'md5': 'e403d2b43fea8e405e88e3f8623909f1',
        'info_dict': {
            'id': '6kmWbXleKW4',
            'ext': 'mp4',
            'title': 'NEU IM SEPTEMBER | Netflix',
            'description': 'md5:2131f3c7525e540d5fd841de938bd452',
            'upload_date': '20170830',
            'uploader': 'Netflix Deutschland, Österreich und Schweiz',
            'uploader_id': 'netflixdach',
        },
        'params': {
            'skip_download': True,
        },
    }, {
        'url': 'https://www.heise.de/video/artikel/nachgehakt-Wie-sichert-das-c-t-Tool-Restric-tor-Windows-10-ab-3700244.html',
        'info_dict': {
            'id': '1_ntrmio2s',
            'ext': 'mp4',
            'title': "nachgehakt: Wie sichert das c't-Tool Restric'tor Windows 10 ab?",
            'description': 'md5:47e8ffb6c46d85c92c310a512d6db271',
            'timestamp': 1512470717,
            'upload_date': '20171205',
        },
        'params': {
            'skip_download': True,
        },
    }, {
        'url': 'https://www.heise.de/ct/artikel/c-t-uplink-20-8-Staubsaugerroboter-Xiaomi-Vacuum-2-AR-Brille-Meta-2-und-Android-rooten-3959893.html',
        'info_dict': {
            'id': '1_59mk80sf',
            'ext': 'mp4',
            'title': "c't uplink 20.8: Staubsaugerroboter Xiaomi Vacuum 2, AR-Brille Meta 2 und Android rooten",
            'description': 'md5:f50fe044d3371ec73a8f79fcebd74afc',
            'timestamp': 1517567237,
            'upload_date': '20180202',
        },
        'params': {
            'skip_download': True,
        },
    }, {
        'url': 'http://www.heise.de/ct/artikel/c-t-uplink-3-3-Owncloud-Tastaturen-Peilsender-Smartphone-2403911.html',
        'only_matching': True,
    }, {
        'url': 'http://www.heise.de/newsticker/meldung/c-t-uplink-Owncloud-Tastaturen-Peilsender-Smartphone-2404251.html?wt_mc=rss.ho.beitrag.atom',
        'only_matching': True,
    }, {
        'url': 'http://www.heise.de/ct/ausgabe/2016-12-Spiele-3214137.html',
        'only_matching': True,
    }]

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

        def extract_title(default=NO_DEFAULT):
            title = self._html_search_meta(
                ('fulltitle', 'title'), webpage, default=None)
            if not title or title == "c't":
                title = self._search_regex(
                    r'
]+class="videoplayerjw"[^>]+data-title="([^"]+)"',
                    webpage, 'title', default=None)
            if not title:
                title = self._html_search_regex(
                    r'
]+\bclass=["\']article_page_title[^>]+>(.+?)<',
                    webpage, 'title', default=default)
            return title

        title = extract_title(default=None)
        description = self._og_search_description(
            webpage, default=None) or self._html_search_meta(
            'description', webpage)

        def _make_kaltura_result(kaltura_url):
            return {
                '_type': 'url_transparent',
                'url': smuggle_url(kaltura_url, {'source_url': url}),
                'ie_key': KalturaIE.ie_key(),
                'title': title,
                'description': description,
            }

        kaltura_url = KalturaIE._extract_url(webpage)
        if kaltura_url:
            return _make_kaltura_result(kaltura_url)

        kaltura_id = self._search_regex(
            r'entry-id=(["\'])(?P<id>(?:(?!\1).)+)\1', webpage, 'kaltura id',
            default=None, group='id')
        if kaltura_id:
            return _make_kaltura_result('kaltura:2238431:%s' % kaltura_id)

        yt_urls = tuple(YoutubeIE._extract_embed_urls(url, webpage))
        if yt_urls:
            return self.playlist_from_matches(
                yt_urls, video_id, title, ie=YoutubeIE.ie_key())

        title = extract_title()

        container_id = self._search_regex(
            r'
]+data-container="([0-9]+)"',
            webpage, 'container ID')

        sequenz_id = self._search_regex(
            r'
]+data-sequenz="([0-9]+)"',
            webpage, 'sequenz ID')

        doc = self._download_xml(
            'http://www.heise.de/videout/feed', video_id, query={
                'container': container_id,
                'sequenz': sequenz_id,
            })

        formats = []
        for source_node in doc.findall('.//{http://rss.jwpcdn.com/}source'):
            label = source_node.attrib['label']
            height = int_or_none(self._search_regex(
                r'^(.*?_)?([0-9]+)p$', label, 'height', default=None))
            video_url = source_node.attrib['file']
            ext = determine_ext(video_url, '')
            formats.append({
                'url': video_url,
                'format_note': label,
                'format_id': '%s_%s' % (ext, label),
                'height': height,
            })
        self._sort_formats(formats)

        return {
            'id': video_id,
            'title': title,
            'description': description,
            'thumbnail': (xpath_text(doc, './/{http://rss.jwpcdn.com/}image')
                          or self._og_search_thumbnail(webpage)),
            'timestamp': parse_iso8601(
                self._html_search_meta('date', webpage)),
            'formats': formats,
        }


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

class HellPornoIE(InfoExtractor):
    _VALID_URL = r'https?://(?:www\.)?hellporno\.(?:com/videos|net/v)/(?P<id>[^/]+)'
    _TESTS = [{
        'url': 'http://hellporno.com/videos/dixie-is-posing-with-naked-ass-very-erotic/',
        'md5': 'f0a46ebc0bed0c72ae8fe4629f7de5f3',
        'info_dict': {
            'id': '149116',
            'display_id': 'dixie-is-posing-with-naked-ass-very-erotic',
            'ext': 'mp4',
            'title': 'Dixie is posing with naked ass very erotic',
            'description': 'md5:9a72922749354edb1c4b6e540ad3d215',
            'categories': list,
            'thumbnail': r're:https?://.*\.jpg$',
            'duration': 240,
            'timestamp': 1398762720,
            'upload_date': '20140429',
            'view_count': int,
            'age_limit': 18,
        },
    }, {
        'url': 'http://hellporno.net/v/186271/',
        'only_matching': True,
    }]

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

        webpage = self._download_webpage(url, display_id)

        title = remove_end(self._html_extract_title(webpage), ' - Hell Porno')

        info = self._parse_html5_media_entries(url, webpage, display_id)[0]
        self._sort_formats(info['formats'])

        video_id = self._search_regex(
            (r'chs_object\s*=\s*["\'](\d+)',
             r'params\[["\']video_id["\']\]\s*=\s*(\d+)'), webpage, 'video id',
            default=display_id)
        description = self._search_regex(
            r'class=["\']desc_video_view_v2[^>]+>([^<]+)', webpage,
            'description', fatal=False)
        categories = [
            c.strip()
            for c in self._html_search_meta(
                'keywords', webpage, 'categories', default='').split(',')
            if c.strip()]
        duration = int_or_none(self._og_search_property(
            'video:duration', webpage, fatal=False))
        timestamp = unified_timestamp(self._og_search_property(
            'video:release_date', webpage, fatal=False))
        view_count = int_or_none(self._search_regex(
            r'>Views\s+(\d+)', webpage, 'view count', fatal=False))

        return merge_dicts(info, {
            'id': video_id,
            'display_id': display_id,
            'title': title,
            'description': description,
            'categories': categories,
            'duration': duration,
            'timestamp': timestamp,
            'view_count': view_count,
            'age_limit': 18,
        })


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

class HelsinkiIE(InfoExtractor):
    IE_DESC = 'helsinki.fi'
    _VALID_URL = r'https?://video\.helsinki\.fi/Arkisto/flash\.php\?id=(?P<id>\d+)'
    _TEST = {
        'url': 'http://video.helsinki.fi/Arkisto/flash.php?id=20258',
        'info_dict': {
            'id': '20258',
            'ext': 'mp4',
            'title': 'Tietotekniikkafoorumi-iltapäivä',
            'description': 'md5:f5c904224d43c133225130fe156a5ee0',
        },
        'params': {
            'skip_download': True,  # RTMP
        }
    }

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

        params = self._parse_json(self._html_search_regex(
            r'(?s)jwplayer\("player"\).setup\((\{.*?\})\);',
            webpage, 'player code'), video_id, transform_source=js_to_json)
        formats = [{
            'url': s['file'],
            'ext': 'mp4',
        } for s in params['sources']]
        self._sort_formats(formats)

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


code:
from .common import InfoExtractor

class HentaiStigmaIE(InfoExtractor):
    _VALID_URL = r'^https?://hentai\.animestigma\.com/(?P<id>[^/]+)'
    _TEST = {
        'url': 'http://hentai.animestigma.com/inyouchuu-etsu-bonus/',
        'md5': '4e3d07422a68a4cc363d8f57c8bf0d23',
        'info_dict': {
            'id': 'inyouchuu-etsu-bonus',
            'ext': 'mp4',
            'title': 'Inyouchuu Etsu Bonus',
            'age_limit': 18,
        }
    }

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

        webpage = self._download_webpage(url, video_id)

        title = self._html_search_regex(
            r'<h2[^>]+class="posttitle"[^>]*><a[^>]*>([^<]+)</a>',
            webpage, 'title')
        wrap_url = self._html_search_regex(
            r'<iframe[^>]+src="([^"]+mp4)"', webpage, 'wrapper url')
        wrap_webpage = self._download_webpage(wrap_url, video_id)

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

        return {
            'id': video_id,
            'url': video_url,
            'title': title,
            'age_limit': 18,
        }


code:
from .common import InfoExtractor

class HGTVComShowIE(InfoExtractor):
    IE_NAME = 'hgtv.com:show'
    _VALID_URL = r'https?://(?:www\.)?hgtv\.com/shows/[^/]+/(?P<id>[^/?#&]+)'
    _TESTS = [{
        # data-module="video"
        'url': 'http://www.hgtv.com/shows/flip-or-flop/flip-or-flop-full-episodes-season-4-videos',
        'info_dict': {
            'id': 'flip-or-flop-full-episodes-season-4-videos',
            'title': 'Flip or Flop Full Episodes',
        },
        'playlist_mincount': 15,
    }, {
        # data-deferred-module="video"
        'url': 'http://www.hgtv.com/shows/good-bones/episodes/an-old-victorian-house-gets-a-new-facelift',
        'only_matching': True,
    }]

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

        webpage = self._download_webpage(url, display_id)

        config = self._parse_json(
            self._search_regex(
                r'(?s)data-(?:deferred-)?module=["\']video["\'][^>]*>.*?<script[^>]+type=["\']text/x-config["\'][^>]*>(.+?)</script',
                webpage, 'video config'),
            display_id)['channels'][0]

        entries = [
            self.url_result(video['releaseUrl'])
            for video in config['videos'] if video.get('releaseUrl')]

        return self.playlist_result(
            entries, display_id, config.get('title'), config.get('description'))


code:
import re

from .common import InfoExtractor
from ..utils import (
    ExtractorError,
    int_or_none,
    try_get,
    url_or_none,
    urlencode_postdata,
)

class HiDiveIE(InfoExtractor):
    _VALID_URL = r'https?://(?:www\.)?hidive\.com/stream/(?P<id>(?P<title>[^/]+)/(?P<key>[^/?#&]+))'
    # Using X-Forwarded-For results in 403 HTTP error for HLS fragments,
    # so disabling geo bypass completely
    _GEO_BYPASS = False
    _NETRC_MACHINE = 'hidive'
    _LOGIN_URL = 'https://www.hidive.com/account/login'

    _TESTS = [{
        'url': 'https://www.hidive.com/stream/the-comic-artist-and-his-assistants/s01e001',
        'info_dict': {
            'id': 'the-comic-artist-and-his-assistants/s01e001',
            'ext': 'mp4',
            'title': 'the-comic-artist-and-his-assistants/s01e001',
            'series': 'the-comic-artist-and-his-assistants',
            'season_number': 1,
            'episode_number': 1,
        },
        'params': {
            'skip_download': True,
        },
        'skip': 'Requires Authentication',
    }]

    def _perform_login(self, username, password):
        webpage = self._download_webpage(self._LOGIN_URL, None)
        form = self._search_regex(
            r'(?s)<form[^>]+action="/account/login"[^>]*>(.+?)</form>',
            webpage, 'login form', default=None)
        if not form:  # logged in
            return
        data = self._hidden_inputs(form)
        data.update({
            'Email': username,
            'Password': password,
        })
        self._download_webpage(
            self._LOGIN_URL, None, 'Logging in', data=urlencode_postdata(data))

    def _call_api(self, video_id, title, key, data={}, **kwargs):
        data = {
            **data,
            'Title': title,
            'Key': key,
            'PlayerId': 'f4f895ce1ca713ba263b91caeb1daa2d08904783',
        }
        return self._download_json(
            'https://www.hidive.com/play/settings', video_id,
            data=urlencode_postdata(data), **kwargs) or {}

    def _extract_subtitles_from_rendition(self, rendition, subtitles, parsed_urls):
        for cc_file in rendition.get('ccFiles', []):
            cc_url = url_or_none(try_get(cc_file, lambda x: x[2]))
            # name is used since we cant distinguish subs with same language code
            cc_lang = try_get(cc_file, (lambda x: x[1].replace(' ', '-').lower(), lambda x: x[0]), str)
            if cc_url not in parsed_urls and cc_lang:
                parsed_urls.add(cc_url)
                subtitles.setdefault(cc_lang, []).append({'url': cc_url})

    def _get_subtitles(self, url, video_id, title, key, parsed_urls):
        webpage = self._download_webpage(url, video_id, fatal=False) or ''
        subtitles = {}
        for caption in set(re.findall(r'data-captions=\"([^\"]+)\"', webpage)):
            renditions = self._call_api(
                video_id, title, key, {'Captions': caption}, fatal=False,
                note=f'Downloading {caption} subtitle information').get('renditions') or {}
            for rendition_id, rendition in renditions.items():
                self._extract_subtitles_from_rendition(rendition, subtitles, parsed_urls)
        return subtitles

    def _real_extract(self, url):
        video_id, title, key = self._match_valid_url(url).group('id', 'title', 'key')
        settings = self._call_api(video_id, title, key)

        restriction = settings.get('restrictionReason')
        if restriction == 'RegionRestricted':
            self.raise_geo_restricted()
        if restriction and restriction != 'None':
            raise ExtractorError(
                '%s said: %s' % (self.IE_NAME, restriction), expected=True)

        formats, parsed_urls = [], {None}
        for rendition_id, rendition in settings['renditions'].items():
            audio, version, extra = rendition_id.split('_')
            m3u8_url = url_or_none(try_get(rendition, lambda x: x['bitrates']['hls']))
            if m3u8_url not in parsed_urls:
                parsed_urls.add(m3u8_url)
                frmt = self._extract_m3u8_formats(
                    m3u8_url, video_id, 'mp4', entry_protocol='m3u8_native', m3u8_id=rendition_id, fatal=False)
                for f in frmt:
                    f['language'] = audio
                    f['format_note'] = f'{version}, {extra}'
                formats.extend(frmt)
        self._sort_formats(formats)

        return {
            'id': video_id,
            'title': video_id,
            'subtitles': self.extract_subtitles(url, video_id, title, key, parsed_urls),
            'formats': formats,
            'series': title,
            'season_number': int_or_none(
                self._search_regex(r's(\d+)', key, 'season number', default=None)),
            'episode_number': int_or_none(
                self._search_regex(r'e(\d+)', key, 'episode number', default=None)),
            'http_headers': {'Referer': url}
        }


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

class HistoricFilmsIE(InfoExtractor):
    _VALID_URL = r'https?://(?:www\.)?historicfilms\.com/(?:tapes/|play)(?P<id>\d+)'
    _TEST = {
        'url': 'http://www.historicfilms.com/tapes/4728',
        'md5': 'd4a437aec45d8d796a38a215db064e9a',
        'info_dict': {
            'id': '4728',
            'ext': 'mov',
            'title': 'Historic Films: GP-7',
            'description': 'md5:1a86a0f3ac54024e419aba97210d959a',
            'thumbnail': r're:^https?://.*\.jpg$',
            'duration': 2096,
        },
    }

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

        webpage = self._download_webpage(url, video_id)

        tape_id = self._search_regex(
            [r'class="tapeId"[^>]*>([^<]+)<', r'tapeId\s*:\s*"([^"]+)"'],
            webpage, 'tape id')

        title = self._og_search_title(webpage)
        description = self._og_search_description(webpage)
        thumbnail = self._html_search_meta(
            'thumbnailUrl', webpage, 'thumbnails') or self._og_search_thumbnail(webpage)
        duration = parse_duration(self._html_search_meta(
            'duration', webpage, 'duration'))

        video_url = 'http://www.historicfilms.com/video/%s_%s_web.mov' % (tape_id, video_id)

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


code:
import re

from .common import InfoExtractor
from ..compat import compat_str
from ..utils import (
    clean_html,
    determine_ext,
    float_or_none,
    int_or_none,
    parse_iso8601,
)

class HitboxIE(InfoExtractor):
    IE_NAME = 'hitbox'
    _VALID_URL = r'https?://(?:www\.)?(?:hitbox|smashcast)\.tv/(?:[^/]+/)*videos?/(?P<id>[0-9]+)'
    _TESTS = [{
        'url': 'http://www.hitbox.tv/video/203213',
        'info_dict': {
            'id': '203213',
            'title': 'hitbox @ gamescom, Sub Button Hype extended, Giveaway - hitbox News Update with Oxy',
            'alt_title': 'hitboxlive - Aug 9th #6',
            'description': '',
            'ext': 'mp4',
            'thumbnail': r're:^https?://.*\.jpg$',
            'duration': 215.1666,
            'resolution': 'HD 720p',
            'uploader': 'hitboxlive',
            'view_count': int,
            'timestamp': 1407576133,
            'upload_date': '20140809',
            'categories': ['Live Show'],
        },
        'params': {
            # m3u8 download
            'skip_download': True,
        },
    }, {
        'url': 'https://www.smashcast.tv/hitboxlive/videos/203213',
        'only_matching': True,
    }]

    def _extract_metadata(self, url, video_id):
        thumb_base = 'https://edge.sf.hitbox.tv'
        metadata = self._download_json(
            '%s/%s' % (url, video_id), video_id, 'Downloading metadata JSON')

        date = 'media_live_since'
        media_type = 'livestream'
        if metadata.get('media_type') == 'video':
            media_type = 'video'
            date = 'media_date_added'

        video_meta = metadata.get(media_type, [])[0]
        title = video_meta.get('media_status')
        alt_title = video_meta.get('media_title')
        description = clean_html(
            video_meta.get('media_description')
            or video_meta.get('media_description_md'))
        duration = float_or_none(video_meta.get('media_duration'))
        uploader = video_meta.get('media_user_name')
        views = int_or_none(video_meta.get('media_views'))
        timestamp = parse_iso8601(video_meta.get(date), ' ')
        categories = [video_meta.get('category_name')]
        thumbs = [{
            'url': thumb_base + video_meta.get('media_thumbnail'),
            'width': 320,
            'height': 180
        }, {
            'url': thumb_base + video_meta.get('media_thumbnail_large'),
            'width': 768,
            'height': 432
        }]

        return {
            'id': video_id,
            'title': title,
            'alt_title': alt_title,
            'description': description,
            'ext': 'mp4',
            'thumbnails': thumbs,
            'duration': duration,
            'uploader': uploader,
            'view_count': views,
            'timestamp': timestamp,
            'categories': categories,
        }

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

        player_config = self._download_json(
            'https://www.smashcast.tv/api/player/config/video/%s' % video_id,
            video_id, 'Downloading video JSON')

        formats = []
        for video in player_config['clip']['bitrates']:
            label = video.get('label')
            if label == 'Auto':
                continue
            video_url = video.get('url')
            if not video_url:
                continue
            bitrate = int_or_none(video.get('bitrate'))
            if determine_ext(video_url) == 'm3u8':
                if not video_url.startswith('http'):
                    continue
                formats.append({
                    'url': video_url,
                    'ext': 'mp4',
                    'tbr': bitrate,
                    'format_note': label,
                    'protocol': 'm3u8_native',
                })
            else:
                formats.append({
                    'url': video_url,
                    'tbr': bitrate,
                    'format_note': label,
                })
        self._sort_formats(formats)

        metadata = self._extract_metadata(
            'https://www.smashcast.tv/api/media/video', video_id)
        metadata['formats'] = formats

        return metadata

class HitboxLiveIE(HitboxIE):
    IE_NAME = 'hitbox:live'
    _VALID_URL = r'https?://(?:www\.)?(?:hitbox|smashcast)\.tv/(?P<id>[^/?#&]+)'
    _TESTS = [{
        'url': 'http://www.hitbox.tv/dimak',
        'info_dict': {
            'id': 'dimak',
            'ext': 'mp4',
            'description': 'md5:c9f80fa4410bc588d7faa40003fc7d0e',
            'timestamp': int,
            'upload_date': compat_str,
            'title': compat_str,
            'uploader': 'Dimak',
        },
        'params': {
            # live
            'skip_download': True,
        },
    }, {
        'url': 'https://www.smashcast.tv/dimak',
        'only_matching': True,
    }]

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

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

        player_config = self._download_json(
            'https://www.smashcast.tv/api/player/config/live/%s' % video_id,
            video_id)

        formats = []
        cdns = player_config.get('cdns')
        servers = []
        for cdn in cdns:
            # Subscribe URLs are not playable
            if cdn.get('rtmpSubscribe') is True:
                continue
            base_url = cdn.get('netConnectionUrl')
            host = re.search(r'.+\.([^\.]+\.[^\./]+)/.+', base_url).group(1)
            if base_url not in servers:
                servers.append(base_url)
                for stream in cdn.get('bitrates'):
                    label = stream.get('label')
                    if label == 'Auto':
                        continue
                    stream_url = stream.get('url')
                    if not stream_url:
                        continue
                    bitrate = int_or_none(stream.get('bitrate'))
                    if stream.get('provider') == 'hls' or determine_ext(stream_url) == 'm3u8':
                        if not stream_url.startswith('http'):
                            continue
                        formats.append({
                            'url': stream_url,
                            'ext': 'mp4',
                            'tbr': bitrate,
                            'format_note': label,
                            'rtmp_live': True,
                        })
                    else:
                        formats.append({
                            'url': '%s/%s' % (base_url, stream_url),
                            'ext': 'mp4',
                            'tbr': bitrate,
                            'rtmp_live': True,
                            'format_note': host,
                            'page_url': url,
                            'player_url': 'http://www.hitbox.tv/static/player/flowplayer/flowplayer.commercial-3.2.16.swf',
                        })
        self._sort_formats(formats)

        metadata = self._extract_metadata(
            'https://www.smashcast.tv/api/media/live', video_id)
        metadata['formats'] = formats
        metadata['is_live'] = True
        metadata['title'] = metadata.get('title')

        return metadata


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

class HitRecordIE(InfoExtractor):
    _VALID_URL = r'https?://(?:www\.)?hitrecord\.org/records/(?P<id>\d+)'
    _TEST = {
        'url': 'https://hitrecord.org/records/2954362',
        'md5': 'fe1cdc2023bce0bbb95c39c57426aa71',
        'info_dict': {
            'id': '2954362',
            'ext': 'mp4',
            'title': 'A Very Different World (HITRECORD x ACLU)',
            'description': 'md5:e62defaffab5075a5277736bead95a3d',
            'duration': 139.327,
            'timestamp': 1471557582,
            'upload_date': '20160818',
            'uploader': 'Zuzi.C12',
            'uploader_id': '362811',
            'view_count': int,
            'like_count': int,
            'comment_count': int,
            'tags': list,
        }
    }

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

        video = self._download_json(
            'https://hitrecord.org/api/web/records/%s' % video_id, video_id)

        title = video['title']
        video_url = video['source_url']['mp4_url']

        tags = None
        tags_list = try_get(video, lambda x: x['tags'], list)
        if tags_list:
            tags = [
                t['text']
                for t in tags_list
                if isinstance(t, dict) and t.get('text')
                and isinstance(t['text'], compat_str)]

        return {
            'id': video_id,
            'url': video_url,
            'title': title,
            'description': clean_html(video.get('body')),
            'duration': float_or_none(video.get('duration'), 1000),
            'timestamp': int_or_none(video.get('created_at_i')),
            'uploader': try_get(
                video, lambda x: x['user']['username'], compat_str),
            'uploader_id': try_get(
                video, lambda x: compat_str(x['user']['id'])),
            'view_count': int_or_none(video.get('total_views_count')),
            'like_count': int_or_none(video.get('hearts_count')),
            'comment_count': int_or_none(video.get('comments_count')),
            'tags': tags,
        }


code:
from .common import InfoExtractor
from ..compat import compat_str
from ..utils import (
    clean_html,
    ExtractorError,
    int_or_none,
    merge_dicts,
    parse_count,
    str_or_none,
    try_get,
    unified_strdate,
    urlencode_postdata,
    urljoin,
)

class HKETVIE(InfoExtractor):
    IE_NAME = 'hketv'
    IE_DESC = '香港教育局教育電視 (HKETV) Educational Television, Hong Kong Educational Bureau'
    _GEO_BYPASS = False
    _GEO_COUNTRIES = ['HK']
    _VALID_URL = r'https?://(?:www\.)?hkedcity\.net/etv/resource/(?P<id>[0-9]+)'
    _TESTS = [{
        'url': 'https://www.hkedcity.net/etv/resource/2932360618',
        'md5': 'f193712f5f7abb208ddef3c5ea6ed0b7',
        'info_dict': {
            'id': '2932360618',
            'ext': 'mp4',
            'title': '喜閱一生(共享閱讀樂) (中、英文字幕可供選擇)',
            'description': 'md5:d5286d05219ef50e0613311cbe96e560',
            'upload_date': '20181024',
            'duration': 900,
            'subtitles': 'count:2',
        },
        'skip': 'Geo restricted to HK',
    }, {
        'url': 'https://www.hkedcity.net/etv/resource/972641418',
        'md5': '1ed494c1c6cf7866a8290edad9b07dc9',
        'info_dict': {
            'id': '972641418',
            'ext': 'mp4',
            'title': '衣冠楚楚 (天使系列之一)',
            'description': 'md5:10bb3d659421e74f58e5db5691627b0f',
            'upload_date': '20070109',
            'duration': 907,
            'subtitles': {},
        },
        'params': {
            'geo_verification_proxy': '<HK proxy here>',
        },
        'skip': 'Geo restricted to HK',
    }]

    _CC_LANGS = {
        '中文（繁體中文）': 'zh-Hant',
        '中文（简体中文）': 'zh-Hans',
        'English': 'en',
        'Bahasa Indonesia': 'id',
        '\u0939\u093f\u0928\u094d\u0926\u0940': 'hi',
        '\u0928\u0947\u092a\u093e\u0932\u0940': 'ne',
        'Tagalog': 'tl',
        '\u0e44\u0e17\u0e22': 'th',
        '\u0627\u0631\u062f\u0648': 'ur',
    }
    _FORMAT_HEIGHTS = {
        'SD': 360,
        'HD': 720,
    }
    _APPS_BASE_URL = 'https://apps.hkedcity.net'

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

        title = (
            self._html_search_meta(
                ('ed_title', 'search.ed_title'), webpage, default=None)
            or self._search_regex(
                r'data-favorite_title_(?:eng|chi)=(["\'])(?P<id>(?:(?!\1).)+)\1',
                webpage, 'title', default=None, group='url')
            or self._html_search_regex(
                r'<h1>([^<]+)</h1>', webpage, 'title', default=None)
            or self._og_search_title(webpage)
        )

        file_id = self._search_regex(
            r'post_var\[["\']file_id["\']\s*\]\s*=\s*(.+?);',
            webpage, 'file ID')
        curr_url = self._search_regex(
            r'post_var\[["\']curr_url["\']\s*\]\s*=\s*"(.+?)";',
            webpage, 'curr URL')
        data = {
            'action': 'get_info',
            'curr_url': curr_url,
            'file_id': file_id,
            'video_url': file_id,
        }

        response = self._download_json(
            self._APPS_BASE_URL + '/media/play/handler.php', video_id,
            data=urlencode_postdata(data),
            headers=merge_dicts({
                'Content-Type': 'application/x-www-form-urlencoded'},
                self.geo_verification_headers()))

        result = response['result']

        if not response.get('success') or not response.get('access'):
            error = clean_html(response.get('access_err_msg'))
            if 'Video streaming is not available in your country' in error:
                self.raise_geo_restricted(
                    msg=error, countries=self._GEO_COUNTRIES)
            else:
                raise ExtractorError(error, expected=True)

        formats = []

        width = int_or_none(result.get('width'))
        height = int_or_none(result.get('height'))

        playlist0 = result['playlist'][0]
        for fmt in playlist0['sources']:
            file_url = urljoin(self._APPS_BASE_URL, fmt.get('file'))
            if not file_url:
                continue
            # If we ever wanted to provide the final resolved URL that
            # does not require cookies, albeit with a shorter lifespan:
            #     urlh = self._downloader.urlopen(file_url)
            #     resolved_url = urlh.geturl()
            label = fmt.get('label')
            h = self._FORMAT_HEIGHTS.get(label)
            w = h * width // height if h and width and height else None
            formats.append({
                'format_id': label,
                'ext': fmt.get('type'),
                'url': file_url,
                'width': w,
                'height': h,
            })
        self._sort_formats(formats)

        subtitles = {}
        tracks = try_get(playlist0, lambda x: x['tracks'], list) or []
        for track in tracks:
            if not isinstance(track, dict):
                continue
            track_kind = str_or_none(track.get('kind'))
            if not track_kind or not isinstance(track_kind, compat_str):
                continue
            if track_kind.lower() not in ('captions', 'subtitles'):
                continue
            track_url = urljoin(self._APPS_BASE_URL, track.get('file'))
            if not track_url:
                continue
            track_label = track.get('label')
            subtitles.setdefault(self._CC_LANGS.get(
                track_label, track_label), []).append({
                    'url': self._proto_relative_url(track_url),
                    'ext': 'srt',
                })

        # Likes
        emotion = self._download_json(
            'https://emocounter.hkedcity.net/handler.php', video_id,
            data=urlencode_postdata({
                'action': 'get_emotion',
                'data[bucket_id]': 'etv',
                'data[identifier]': video_id,
            }),
            headers={'Content-Type': 'application/x-www-form-urlencoded'},
            fatal=False) or {}
        like_count = int_or_none(try_get(
            emotion, lambda x: x['data']['emotion_data'][0]['count']))

        return {
            'id': video_id,
            'title': title,
            'description': self._html_search_meta(
                'description', webpage, fatal=False),
            'upload_date': unified_strdate(self._html_search_meta(
                'ed_date', webpage, fatal=False), day_first=False),
            'duration': int_or_none(result.get('length')),
            'formats': formats,
            'subtitles': subtitles,
            'thumbnail': urljoin(self._APPS_BASE_URL, result.get('image')),
            'view_count': parse_count(result.get('view_count')),
            'like_count': like_count,
        }


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

class HolodexIE(InfoExtractor):
    _VALID_URL = r'''(?x)https?://(?:www\.|staging\.)?holodex\.net/(?:
            api/v2/playlist/(?P<playlist>\d+)|
            watch/(?P<id>\w+)(?:\?(?:[^#]+&)?playlist=(?P<playlist2>\d+))?
        )'''
    _TESTS = [{
        'url': 'https://holodex.net/watch/9kQ2GtvDV3s',
        'md5': 'be5ffce2f0feae8ba4c01553abc0f175',
        'info_dict': {
            'ext': 'mp4',
            'id': '9kQ2GtvDV3s',
            'title': '【おちゃめ機能】ホロライブが吹っ切れた【24人で歌ってみた】',
            'channel_id': 'UCJFZiqLMntJufDCHc6bQixg',
            'playable_in_embed': True,
            'tags': 'count:43',
            'age_limit': 0,
            'live_status': 'not_live',
            'description': 'md5:040e866c09dc4ab899b36479f4b7c7a2',
            'channel_url': 'https://www.youtube.com/channel/UCJFZiqLMntJufDCHc6bQixg',
            'upload_date': '20200406',
            'uploader_url': 'http://www.youtube.com/channel/UCJFZiqLMntJufDCHc6bQixg',
            'view_count': int,
            'channel': 'hololive ホロライブ - VTuber Group',
            'categories': ['Music'],
            'uploader': 'hololive ホロライブ - VTuber Group',
            'channel_follower_count': int,
            'uploader_id': 'UCJFZiqLMntJufDCHc6bQixg',
            'availability': 'public',
            'thumbnail': 'https://i.ytimg.com/vi_webp/9kQ2GtvDV3s/maxresdefault.webp',
            'duration': 263,
            'like_count': int,
        },
    }, {
        'url': 'https://holodex.net/api/v2/playlist/239',
        'info_dict': {
            'id': '239',
            'title': 'Songs/Videos that made fall into the rabbit hole (from my google activity history)',
        },
        'playlist_count': 14,
    }, {
        'url': 'https://holodex.net/watch/_m2mQyaofjI?foo=bar&playlist=69',
        'info_dict': {
            'id': '69',
            'title': '拿著金斧頭的藍髮大姊姊'
        },
        'playlist_count': 3,
    }, {
        'url': 'https://holodex.net/watch/_m2mQyaofjI?playlist=69',
        'info_dict': {
            'id': '_m2mQyaofjI',
            'ext': 'mp4',
            'playable_in_embed': True,
            'like_count': int,
            'uploader': 'Ernst / エンスト',
            'duration': 11,
            'uploader_url': 'http://www.youtube.com/channel/UCqSX4PPZY0cyetqKVY_wRVA',
            'categories': ['Entertainment'],
            'title': '【星街すいせい】星街向你獻上晚安',
            'upload_date': '20210705',
            'description': 'md5:8b8ffb157bae77f2d109021a0b577d4a',
            'channel': 'Ernst / エンスト',
            'channel_id': 'UCqSX4PPZY0cyetqKVY_wRVA',
            'channel_follower_count': int,
            'view_count': int,
            'tags': [],
            'live_status': 'not_live',
            'channel_url': 'https://www.youtube.com/channel/UCqSX4PPZY0cyetqKVY_wRVA',
            'availability': 'public',
            'thumbnail': 'https://i.ytimg.com/vi_webp/_m2mQyaofjI/maxresdefault.webp',
            'age_limit': 0,
            'uploader_id': 'UCqSX4PPZY0cyetqKVY_wRVA',
            'comment_count': int,
        },
        'params': {'noplaylist': True},
    }, {
        'url': 'https://staging.holodex.net/api/v2/playlist/125',
        'only_matching': True,
    }, {
        'url': 'https://staging.holodex.net/watch/rJJTJA_T_b0?playlist=25',
        'only_matching': True,
    }, {
        'url': 'https://staging.holodex.net/watch/s1ifBeukThg',
        'only_matching': True,
    }]

    def _real_extract(self, url):
        video_id, playlist_id, pl_id2 = self._match_valid_url(url).group('id', 'playlist', 'playlist2')
        playlist_id = playlist_id or pl_id2

        if not self._yes_playlist(playlist_id, video_id):
            return self.url_result(f'https://www.youtube.com/watch?v={video_id}', YoutubeIE)

        data = self._download_json(f'https://holodex.net/api/v2/playlist/{playlist_id}', playlist_id)
        return self.playlist_from_matches(
            traverse_obj(data, ('videos', ..., 'id')), playlist_id, data.get('name'), ie=YoutubeIE)


code:
from .common import InfoExtractor
from ..compat import compat_b64decode
from ..utils import (
    ExtractorError,
    HEADRequest,
    sanitized_Request,
    urlencode_postdata,
)

class HotNewHipHopIE(InfoExtractor):
    _VALID_URL = r'https?://(?:www\.)?hotnewhiphop\.com/.*\.(?P<id>.*)\.html'
    _TEST = {
        'url': 'http://www.hotnewhiphop.com/freddie-gibbs-lay-it-down-song.1435540.html',
        'md5': '2c2cd2f76ef11a9b3b581e8b232f3d96',
        'info_dict': {
            'id': '1435540',
            'ext': 'mp3',
            'title': 'Freddie Gibbs - Lay It Down'
        }
    }

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

        video_url_base64 = self._search_regex(
            r'data-path="(.*?)"', webpage, 'video URL', default=None)

        if video_url_base64 is None:
            video_url = self._search_regex(
                r'"contentUrl" content="(.*?)"', webpage, 'content URL')
            return self.url_result(video_url, ie='Youtube')

        reqdata = urlencode_postdata([
            ('mediaType', 's'),
            ('mediaId', video_id),
        ])
        r = sanitized_Request(
            'http://www.hotnewhiphop.com/ajax/media/getActions/', data=reqdata)
        r.add_header('Content-Type', 'application/x-www-form-urlencoded')
        mkd = self._download_json(
            r, video_id, note='Requesting media key',
            errnote='Could not download media key')
        if 'mediaKey' not in mkd:
            raise ExtractorError('Did not get a media key')

        redirect_url = compat_b64decode(video_url_base64).decode('utf-8')
        redirect_req = HEADRequest(redirect_url)
        req = self._request_webpage(
            redirect_req, video_id,
            note='Resolving final URL', errnote='Could not resolve final URL')
        video_url = req.geturl()
        if video_url.endswith('.html'):
            raise ExtractorError('Redirect failed')

        video_title = self._og_search_title(webpage).strip()

        return {
            'id': video_id,
            'url': video_url,
            'title': video_title,
            'thumbnail': self._og_search_thumbnail(webpage),
        }


code:
import hashlib
import hmac
import re
import time
import uuid
import json

from .common import InfoExtractor
from ..compat import (
    compat_HTTPError,
    compat_str
)
from ..utils import (
    determine_ext,
    ExtractorError,
    int_or_none,
    join_nonempty,
    str_or_none,
    try_get,
    url_or_none,
)

class HotStarBaseIE(InfoExtractor):
    _BASE_URL = 'https://www.hotstar.com'
    _API_URL = 'https://api.hotstar.com'
    _AKAMAI_ENCRYPTION_KEY = b'\x05\xfc\x1a\x01\xca\xc9\x4b\xc4\x12\xfc\x53\x12\x07\x75\xf9\xee'

    def _call_api_impl(self, path, video_id, query, st=None, cookies=None):
        st = int_or_none(st) or int(time.time())
        exp = st + 6000
        auth = 'st=%d~exp=%d~acl=/*' % (st, exp)
        auth += '~hmac=' + hmac.new(self._AKAMAI_ENCRYPTION_KEY, auth.encode(), hashlib.sha256).hexdigest()

        if cookies and cookies.get('userUP'):
            token = cookies.get('userUP').value
        else:
            token = self._download_json(
                f'{self._API_URL}/um/v3/users',
                video_id, note='Downloading token',
                data=json.dumps({"device_ids": [{"id": compat_str(uuid.uuid4()), "type": "device_id"}]}).encode('utf-8'),
                headers={
                    'hotstarauth': auth,
                    'x-hs-platform': 'PCTV',  # or 'web'
                    'Content-Type': 'application/json',
                })['user_identity']

        response = self._download_json(
            f'{self._API_URL}/{path}', video_id, query=query,
            headers={
                'hotstarauth': auth,
                'x-hs-appversion': '6.72.2',
                'x-hs-platform': 'web',
                'x-hs-usertoken': token,
            })

        if response['message'] != "Playback URL's fetched successfully":
            raise ExtractorError(
                response['message'], expected=True)
        return response['data']

    def _call_api(self, path, video_id, query_name='contentId'):
        return self._download_json(
            f'{self._API_URL}/{path}', video_id=video_id,
            query={
                query_name: video_id,
                'tas': 10000,
            }, headers={
                'x-country-code': 'IN',
                'x-platform-code': 'PCTV',
            })

    def _call_api_v2(self, path, video_id, st=None, cookies=None):
        return self._call_api_impl(
            f'{path}/content/{video_id}', video_id, st=st, cookies=cookies, query={
                'desired-config': 'audio_channel:stereo|container:fmp4|dynamic_range:hdr|encryption:plain|ladder:tv|package:dash|resolution:fhd|subs-tag:HotstarVIP|video_codec:h265',
                'device-id': cookies.get('device_id').value if cookies.get('device_id') else compat_str(uuid.uuid4()),
                'os-name': 'Windows',
                'os-version': '10',
            })

class HotStarIE(HotStarBaseIE):
    IE_NAME = 'hotstar'
    _VALID_URL = r'''(?x)
        https?://(?:www\.)?hotstar\.com(?:/in)?/(?!in/)
        (?:
            (?P<type>movies|sports|episode|(?P<tv>tv))/
            (?(tv)(?:[^/?#]+/){2}|[^?#]*)
        )?
        [^/?#]+/
        (?P<id>\d{10})
    '''

    _TESTS = [{
        'url': 'https://www.hotstar.com/can-you-not-spread-rumours/1000076273',
        'info_dict': {
            'id': '1000076273',
            'ext': 'mp4',
            'title': 'Can You Not Spread Rumours?',
            'description': 'md5:c957d8868e9bc793ccb813691cc4c434',
            'timestamp': 1447248600,
            'upload_date': '20151111',
            'duration': 381,
            'episode': 'Can You Not Spread Rumours?',
        },
    }, {
        'url': 'https://www.hotstar.com/tv/ek-bhram-sarvagun-sampanna/s-2116/janhvi-targets-suman/1000234847',
        'info_dict': {
            'id': '1000234847',
            'ext': 'mp4',
            'title': 'Janhvi Targets Suman',
            'description': 'md5:78a85509348910bd1ca31be898c5796b',
            'timestamp': 1556670600,
            'upload_date': '20190501',
            'duration': 1219,
            'channel': 'StarPlus',
            'channel_id': 3,
            'series': 'Ek Bhram - Sarvagun Sampanna',
            'season': 'Chapter 1',
            'season_number': 1,
            'season_id': 6771,
            'episode': 'Janhvi Targets Suman',
            'episode_number': 8,
        }
    }, {
        'url': 'https://www.hotstar.com/movies/radha-gopalam/1000057157',
        'only_matching': True,
    }, {
        'url': 'https://www.hotstar.com/in/sports/cricket/follow-the-blues-2021/recap-eng-fight-back-on-day-2/1260066104',
        'only_matching': True,
    }, {
        'url': 'https://www.hotstar.com/in/sports/football/most-costly-pl-transfers-ft-grealish/1260065956',
        'only_matching': True,
    }]
    _GEO_BYPASS = False

    _TYPE = {
        'movies': 'movie',
        'sports': 'match',
        'episode': 'episode',
        'tv': 'episode',
        None: 'content',
    }

    _IGNORE_MAP = {
        'res': 'resolution',
        'vcodec': 'video_codec',
        'dr': 'dynamic_range',
    }

    @classmethod
    def _video_url(cls, video_id, video_type=None, *, slug='ignore_me', root=None):
        assert None in (video_type, root)
        if not root:
            root = join_nonempty(cls._BASE_URL, video_type, delim='/')
        return f'{root}/{slug}/{video_id}'

    def _real_extract(self, url):
        video_id, video_type = self._match_valid_url(url).group('id', 'type')
        video_type = self._TYPE.get(video_type, video_type)
        cookies = self._get_cookies(url)  # Cookies before any request

        video_data = self._call_api(f'o/v1/{video_type}/detail', video_id)['body']['results']['item']
        if not self.get_param('allow_unplayable_formats') and video_data.get('drmProtected'):
            self.report_drm(video_id)

        # See https://github.com/yt-dlp/yt-dlp/issues/396
        st = self._download_webpage_handle(f'{self._BASE_URL}/in', video_id)[1].headers.get('x-origin-date')

        geo_restricted = False
        formats, subs = [], {}
        headers = {'Referer': f'{self._BASE_URL}/in'}

        # change to v2 in the future
        playback_sets = self._call_api_v2('play/v1/playback', video_id, st=st, cookies=cookies)['playBackSets']
        for playback_set in playback_sets:
            if not isinstance(playback_set, dict):
                continue
            tags = str_or_none(playback_set.get('tagsCombination')) or ''
            if any(f'{prefix}:{ignore}' in tags
                   for key, prefix in self._IGNORE_MAP.items()
                   for ignore in self._configuration_arg(key)):
                continue

            format_url = url_or_none(playback_set.get('playbackUrl'))
            if not format_url:
                continue
            format_url = re.sub(r'(?<=//staragvod)(\d)', r'web\1', format_url)
            dr = re.search(r'dynamic_range:(?P<dr>[a-z]+)', playback_set.get('tagsCombination')).group('dr')
            ext = determine_ext(format_url)

            current_formats, current_subs = [], {}
            try:
                if 'package:hls' in tags or ext == 'm3u8':
                    current_formats, current_subs = self._extract_m3u8_formats_and_subtitles(
                        format_url, video_id, 'mp4',
                        entry_protocol='m3u8_native',
                        m3u8_id=f'{dr}-hls', headers=headers)
                elif 'package:dash' in tags or ext == 'mpd':
                    current_formats, current_subs = self._extract_mpd_formats_and_subtitles(
                        format_url, video_id, mpd_id=f'{dr}-dash', headers=headers)
                elif ext == 'f4m':
                    pass  # XXX: produce broken files
                else:
                    current_formats = [{
                        'url': format_url,
                        'width': int_or_none(playback_set.get('width')),
                        'height': int_or_none(playback_set.get('height')),
                    }]
            except ExtractorError as e:
                if isinstance(e.cause, compat_HTTPError) and e.cause.code == 403:
                    geo_restricted = True
                continue

            if tags and 'encryption:plain' not in tags:
                for f in current_formats:
                    f['has_drm'] = True
            if tags and 'language' in tags:
                lang = re.search(r'language:(?P<lang>[a-z]+)', tags).group('lang')
                for f in current_formats:
                    if not f.get('langauge'):
                        f['language'] = lang

            formats.extend(current_formats)
            subs = self._merge_subtitles(subs, current_subs)

        if not formats and geo_restricted:
            self.raise_geo_restricted(countries=['IN'], metadata_available=True)
        self._sort_formats(formats)
        for f in formats:
            f.setdefault('http_headers', {}).update(headers)

        return {
            'id': video_id,
            'title': video_data.get('title'),
            'description': video_data.get('description'),
            'duration': int_or_none(video_data.get('duration')),
            'timestamp': int_or_none(video_data.get('broadcastDate') or video_data.get('startDate')),
            'formats': formats,
            'subtitles': subs,
            'channel': video_data.get('channelName'),
            'channel_id': video_data.get('channelId'),
            'series': video_data.get('showName'),
            'season': video_data.get('seasonName'),
            'season_number': int_or_none(video_data.get('seasonNo')),
            'season_id': video_data.get('seasonId'),
            'episode': video_data.get('title'),
            'episode_number': int_or_none(video_data.get('episodeNo')),
        }

class HotStarPrefixIE(InfoExtractor):
    """ The "hotstar:" prefix is no longer in use, but this is kept for backward compatibility """
    IE_DESC = False
    _VALID_URL = r'hotstar:(?:(?P<type>\w+):)?(?P<id>\d+)$'
    _TESTS = [{
        'url': 'hotstar:1000076273',
        'only_matching': True,
    }, {
        'url': 'hotstar:movies:1000057157',
        'info_dict': {
            'id': '1000057157',
            'ext': 'mp4',
            'title': 'Radha Gopalam',
            'description': 'md5:be3bc342cc120bbc95b3b0960e2b0d22',
            'timestamp': 1140805800,
            'upload_date': '20060224',
            'duration': 9182,
            'episode': 'Radha Gopalam',
        },
    }, {
        'url': 'hotstar:episode:1000234847',
        'only_matching': True,
    }, {
        # contentData
        'url': 'hotstar:sports:1260065956',
        'only_matching': True,
    }, {
        # contentData
        'url': 'hotstar:sports:1260066104',
        'only_matching': True,
    }]

    def _real_extract(self, url):
        video_id, video_type = self._match_valid_url(url).group('id', 'type')
        return self.url_result(HotStarIE._video_url(video_id, video_type), HotStarIE, video_id)

class HotStarPlaylistIE(HotStarBaseIE):
    IE_NAME = 'hotstar:playlist'
    _VALID_URL = r'https?://(?:www\.)?hotstar\.com/tv/[^/]+/s-\w+/list/[^/]+/t-(?P<id>\w+)'
    _TESTS = [{
        'url': 'https://www.hotstar.com/tv/savdhaan-india/s-26/list/popular-clips/t-3_2_26',
        'info_dict': {
            'id': '3_2_26',
        },
        'playlist_mincount': 20,
    }, {
        'url': 'https://www.hotstar.com/tv/savdhaan-india/s-26/list/extras/t-2480',
        'only_matching': True,
    }]

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

        collection = self._call_api('o/v1/tray/find', playlist_id, 'uqId')['body']['results']
        entries = [
            self.url_result(HotStarIE._video_url(video['contentId']), HotStarIE, video['contentId'])
            for video in collection['assets']['items'] if video.get('contentId')]

        return self.playlist_result(entries, playlist_id)

class HotStarSeriesIE(HotStarBaseIE):
    IE_NAME = 'hotstar:series'
    _VALID_URL = r'(?P<url>https?://(?:www\.)?hotstar\.com(?:/in)?/tv/[^/]+/(?P<id>\d+))'
    _TESTS = [{
        'url': 'https://www.hotstar.com/in/tv/radhakrishn/1260000646',
        'info_dict': {
            'id': '1260000646',
        },
        'playlist_mincount': 690,
    }, {
        'url': 'https://www.hotstar.com/tv/dancee-/1260050431',
        'info_dict': {
            'id': '1260050431',
        },
        'playlist_mincount': 43,
    }, {
        'url': 'https://www.hotstar.com/in/tv/mahabharat/435/',
        'info_dict': {
            'id': '435',
        },
        'playlist_mincount': 269,
    }]

    def _real_extract(self, url):
        url, series_id = self._match_valid_url(url).groups()
        headers = {
            'x-country-code': 'IN',
            'x-platform-code': 'PCTV',
        }
        detail_json = self._download_json(
            f'{self._API_URL}/o/v1/show/detail?contentId={series_id}', series_id, headers=headers)
        id = try_get(detail_json, lambda x: x['body']['results']['item']['id'], int)
        item_json = self._download_json(
            f'{self._API_URL}/o/v1/tray/g/1/items?etid=0&tao=0&tas=10000&eid={id}', series_id, headers=headers)

        return self.playlist_result([
            self.url_result(HotStarIE._video_url(video['contentId'], root=url), HotStarIE, video['contentId'])
            for video in item_json['body']['results']['items'] if video.get('contentId')
        ], series_id)


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

class HowcastIE(InfoExtractor):
    _VALID_URL = r'https?://(?:www\.)?howcast\.com/videos/(?P<id>\d+)'
    _TEST = {
        'url': 'http://www.howcast.com/videos/390161-How-to-Tie-a-Square-Knot-Properly',
        'md5': '7d45932269a288149483144f01b99789',
        'info_dict': {
            'id': '390161',
            'ext': 'mp4',
            'title': 'How to Tie a Square Knot Properly',
            'description': 'md5:dbe792e5f6f1489027027bf2eba188a3',
            'timestamp': 1276081287,
            'upload_date': '20100609',
            'duration': 56.823,
        },
        'params': {
            'skip_download': True,
        },
        'add_ie': ['Ooyala'],
    }

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

        webpage = self._download_webpage(url, video_id)

        embed_code = self._search_regex(
            r'<iframe[^>]+src="[^"]+\bembed_code=([^\b]+)\b',
            webpage, 'ooyala embed code')

        return {
            '_type': 'url_transparent',
            'ie_key': 'Ooyala',
            'url': 'ooyala:%s' % embed_code,
            'id': video_id,
            'timestamp': parse_iso8601(self._html_search_meta(
                'article:published_time', webpage, 'timestamp')),
        }


code:
from .common import InfoExtractor
from ..utils import (
    find_xpath_attr,
    int_or_none,
    js_to_json,
    unescapeHTML,
    determine_ext,
)

class HowStuffWorksIE(InfoExtractor):
    _VALID_URL = r'https?://[\da-z-]+\.(?:howstuffworks|stuff(?:(?:youshould|theydontwantyouto)know|toblowyourmind|momnevertoldyou)|(?:brain|car)stuffshow|fwthinking|geniusstuff)\.com/(?:[^/]+/)*(?:\d+-)?(?P<id>.+?)-video\.htm'
    _TESTS = [
        {
            'url': 'http://www.stufftoblowyourmind.com/videos/optical-illusions-video.htm',
            'md5': '76646a5acc0c92bf7cd66751ca5db94d',
            'info_dict': {
                'id': '855410',
                'ext': 'mp4',
                'title': 'Your Trickster Brain: Optical Illusions -- Science on the Web',
                'description': 'md5:e374ff9561f6833ad076a8cc0a5ab2fb',
            },
        },
        {
            'url': 'http://shows.howstuffworks.com/more-shows/why-does-balloon-stick-to-hair-video.htm',
            'only_matching': True,
        }
    ]

    def _real_extract(self, url):
        display_id = self._match_id(url)
        webpage = self._download_webpage(url, display_id)
        clip_js = self._search_regex(
            r'(?s)var clip = ({.*?});', webpage, 'clip info')
        clip_info = self._parse_json(
            clip_js, display_id, transform_source=js_to_json)

        video_id = clip_info['content_id']
        formats = []
        m3u8_url = clip_info.get('m3u8')
        if m3u8_url and determine_ext(m3u8_url) == 'm3u8':
            formats.extend(self._extract_m3u8_formats(m3u8_url, video_id, 'mp4', format_id='hls', fatal=True))
        flv_url = clip_info.get('flv_url')
        if flv_url:
            formats.append({
                'url': flv_url,
                'format_id': 'flv',
            })
        for video in clip_info.get('mp4', []):
            formats.append({
                'url': video['src'],
                'format_id': 'mp4-%s' % video['bitrate'],
                'vbr': int_or_none(video['bitrate'].rstrip('k')),
            })

        if not formats:
            smil = self._download_xml(
                'http://services.media.howstuffworks.com/videos/%s/smil-service.smil' % video_id,
                video_id, 'Downloading video SMIL')

            http_base = find_xpath_attr(
                smil,
                './{0}head/{0}meta'.format('{http://www.w3.org/2001/SMIL20/Language}'),
                'name',
                'httpBase').get('content')

            URL_SUFFIX = '?v=2.11.3&fp=LNX 11,2,202,356&r=A&g=A'

            for video in smil.findall(
                    './{0}body/{0}switch/{0}video'.format('{http://www.w3.org/2001/SMIL20/Language}')):
                vbr = int_or_none(video.attrib['system-bitrate'], scale=1000)
                formats.append({
                    'url': '%s/%s%s' % (http_base, video.attrib['src'], URL_SUFFIX),
                    'format_id': '%dk' % vbr,
                    'vbr': vbr,
                })

        self._sort_formats(formats)

        return {
            'id': '%s' % video_id,
            'display_id': display_id,
            'title': unescapeHTML(clip_info['clip_title']),
            'description': unescapeHTML(clip_info.get('caption')),
            'thumbnail': clip_info.get('video_still_url'),
            'duration': int_or_none(clip_info.get('duration')),
            'formats': formats,
        }


code:
import json
import re

from ..utils import int_or_none, unified_timestamp, unescapeHTML
from .common import InfoExtractor

class HRFernsehenIE(InfoExtractor):
    IE_NAME = 'hrfernsehen'
    _VALID_URL = r'^https?://www\.(?:hr-fernsehen|hessenschau)\.de/.*,video-(?P<id>[0-9]{6})\.html'

    _TESTS = [{
        'url': 'https://www.hessenschau.de/tv-sendung/hessenschau-vom-26082020,video-130546.html',
        'md5': '5c4e0ba94677c516a2f65a84110fc536',
        'info_dict': {
            'id': '130546',
            'ext': 'mp4',
            'description': 'Sturmtief Kirsten fegt über Hessen / Die Corona-Pandemie – eine Chronologie / '
                           'Sterbehilfe: Die Lage in Hessen / Miss Hessen leitet zwei eigene Unternehmen / '
                           'Pop-Up Museum zeigt Schwarze Unterhaltung und Black Music',
            'subtitles': {'de': [{
                'url': 'https://hr-a.akamaihd.net/video/as/hessenschau/2020_08/hrLogo_200826200407_L385592_512x288-25p-500kbit.vtt'
            }]},
            'timestamp': 1598470200,
            'upload_date': '20200826',
            'thumbnail': 'https://www.hessenschau.de/tv-sendung/hs_ganz-1554~_t-1598465545029_v-16to9__medium.jpg',
            'title': 'hessenschau vom 26.08.2020'
        }
    }, {
        'url': 'https://www.hr-fernsehen.de/sendungen-a-z/mex/sendungen/fair-und-gut---was-hinter-aldis-eigenem-guetesiegel-steckt,video-130544.html',
        'only_matching': True
    }]

    _GEO_COUNTRIES = ['DE']

    def extract_airdate(self, loader_data):
        airdate_str = loader_data.get('mediaMetadata', {}).get('agf', {}).get('airdate')

        if airdate_str is None:
            return None

        return unified_timestamp(airdate_str)

    def extract_formats(self, loader_data):
        stream_formats = []
        for stream_obj in loader_data["videoResolutionLevels"]:
            stream_format = {
                'format_id': str(stream_obj['verticalResolution']) + "p",
                'height': stream_obj['verticalResolution'],
                'url': stream_obj['url'],
            }

            quality_information = re.search(r'([0-9]{3,4})x([0-9]{3,4})-([0-9]{2})p-([0-9]{3,4})kbit',
                                            stream_obj['url'])
            if quality_information:
                stream_format['width'] = int_or_none(quality_information.group(1))
                stream_format['height'] = int_or_none(quality_information.group(2))
                stream_format['fps'] = int_or_none(quality_information.group(3))
                stream_format['tbr'] = int_or_none(quality_information.group(4))

            stream_formats.append(stream_format)

        self._sort_formats(stream_formats)
        return stream_formats

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

        title = self._html_search_meta(
            ['og:title', 'twitter:title', 'name'], webpage)
        description = self._html_search_meta(
            ['description'], webpage)

        loader_str = unescapeHTML(self._search_regex(r"data-new-hr-mediaplayer-loader='([^']*)'", webpage, "ardloader"))
        loader_data = json.loads(loader_str)

        info = {
            'id': video_id,
            'title': title,
            'description': description,
            'formats': self.extract_formats(loader_data),
            'timestamp': self.extract_airdate(loader_data)
        }

        if "subtitle" in loader_data:
            info["subtitles"] = {"de": [{"url": loader_data["subtitle"]}]}

        thumbnails = list(set([t for t in loader_data.get("previewImageUrl", {}).values()]))
        if len(thumbnails) > 0:
            info["thumbnails"] = [{"url": t} for t in thumbnails]

        return info


code:
import json

from .common import InfoExtractor
from ..compat import compat_HTTPError
from ..utils import (
    clean_html,
    ExtractorError,
    int_or_none,
    parse_age_limit,
    sanitized_Request,
    try_get,
)

class HRTiBaseIE(InfoExtractor):
    """
        Base Information Extractor for Croatian Radiotelevision
        video on demand site https://hrti.hrt.hr
        Reverse engineered from the JavaScript app in app.min.js
    """
    _NETRC_MACHINE = 'hrti'

    _APP_LANGUAGE = 'hr'
    _APP_VERSION = '1.1'
    _APP_PUBLICATION_ID = 'all_in_one'
    _API_URL = 'http://clientapi.hrt.hr/client_api.php/config/identify/format/json'
    _token = None

    def _initialize_pre_login(self):
        init_data = {
            'application_publication_id': self._APP_PUBLICATION_ID
        }

        uuid = self._download_json(
            self._API_URL, None, note='Downloading uuid',
            errnote='Unable to download uuid',
            data=json.dumps(init_data).encode('utf-8'))['uuid']

        app_data = {
            'uuid': uuid,
            'application_publication_id': self._APP_PUBLICATION_ID,
            'application_version': self._APP_VERSION
        }

        req = sanitized_Request(self._API_URL, data=json.dumps(app_data).encode('utf-8'))
        req.get_method = lambda: 'PUT'

        resources = self._download_json(
            req, None, note='Downloading session information',
            errnote='Unable to download session information')

        self._session_id = resources['session_id']

        modules = resources['modules']

        self._search_url = modules['vod_catalog']['resources']['search']['uri'].format(
            language=self._APP_LANGUAGE,
            application_id=self._APP_PUBLICATION_ID)

        self._login_url = (modules['user']['resources']['login']['uri']
                           + '/format/json').format(session_id=self._session_id)

        self._logout_url = modules['user']['resources']['logout']['uri']

    def _perform_login(self, username, password):
        auth_data = {
            'username': username,
            'password': password,
        }

        try:
            auth_info = self._download_json(
                self._login_url, None, note='Logging in', errnote='Unable to log in',
                data=json.dumps(auth_data).encode('utf-8'))
        except ExtractorError as e:
            if isinstance(e.cause, compat_HTTPError) and e.cause.code == 406:
                auth_info = self._parse_json(e.cause.read().encode('utf-8'), None)
            else:
                raise

        error_message = auth_info.get('error', {}).get('message')
        if error_message:
            raise ExtractorError(
                '%s said: %s' % (self.IE_NAME, error_message),
                expected=True)

        self._token = auth_info['secure_streaming_token']

    def _real_initialize(self):
        if not self._token:
            # TODO: figure out authentication with cookies
            self.raise_login_required(method='password')

class HRTiIE(HRTiBaseIE):
    _VALID_URL = r'''(?x)
                        (?:
                            hrti:(?P<short_id>[0-9]+)|
                            https?://
                                hrti\.hrt\.hr/(?:\#/)?video/show/(?P<id>[0-9]+)/(?P<display_id>[^/]+)?
                        )
                    '''
    _TESTS = [{
        'url': 'https://hrti.hrt.hr/#/video/show/2181385/republika-dokumentarna-serija-16-hd',
        'info_dict': {
            'id': '2181385',
            'display_id': 'republika-dokumentarna-serija-16-hd',
            'ext': 'mp4',
            'title': 'REPUBLIKA, dokumentarna serija (1/6) (HD)',
            'description': 'md5:48af85f620e8e0e1df4096270568544f',
            'duration': 2922,
            'view_count': int,
            'average_rating': int,
            'episode_number': int,
            'season_number': int,
            'age_limit': 12,
        },
        'skip': 'Requires account credentials',
    }, {
        'url': 'https://hrti.hrt.hr/#/video/show/2181385/',
        'only_matching': True,
    }, {
        'url': 'hrti:2181385',
        'only_matching': True,
    }, {
        'url': 'https://hrti.hrt.hr/video/show/3873068/cuvar-dvorca-dramska-serija-14',
        'only_matching': True,
    }]

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

        video = self._download_json(
            '%s/video_id/%s/format/json' % (self._search_url, video_id),
            display_id, 'Downloading video metadata JSON')['video'][0]

        title_info = video['title']
        title = title_info['title_long']

        movie = video['video_assets']['movie'][0]
        m3u8_url = movie['url'].format(TOKEN=self._token)
        formats = self._extract_m3u8_formats(
            m3u8_url, display_id, 'mp4', entry_protocol='m3u8_native',
            m3u8_id='hls')
        self._sort_formats(formats)

        description = clean_html(title_info.get('summary_long'))
        age_limit = parse_age_limit(video.get('parental_control', {}).get('rating'))
        view_count = int_or_none(video.get('views'))
        average_rating = int_or_none(video.get('user_rating'))
        duration = int_or_none(movie.get('duration'))

        return {
            'id': video_id,
            'display_id': display_id,
            'title': title,
            'description': description,
            'duration': duration,
            'view_count': view_count,
            'average_rating': average_rating,
            'age_limit': age_limit,
            'formats': formats,
        }

class HRTiPlaylistIE(HRTiBaseIE):
    _VALID_URL = r'https?://hrti\.hrt\.hr/(?:#/)?video/list/category/(?P<id>[0-9]+)/(?P<display_id>[^/]+)?'
    _TESTS = [{
        'url': 'https://hrti.hrt.hr/#/video/list/category/212/ekumena',
        'info_dict': {
            'id': '212',
            'title': 'ekumena',
        },
        'playlist_mincount': 8,
        'skip': 'Requires account credentials',
    }, {
        'url': 'https://hrti.hrt.hr/#/video/list/category/212/',
        'only_matching': True,
    }, {
        'url': 'https://hrti.hrt.hr/video/list/category/212/ekumena',
        'only_matching': True,
    }]

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

        response = self._download_json(
            '%s/category_id/%s/format/json' % (self._search_url, category_id),
            display_id, 'Downloading video metadata JSON')

        video_ids = try_get(
            response, lambda x: x['video_listings'][0]['alternatives'][0]['list'],
            list) or [video['id'] for video in response.get('videos', []) if video.get('id')]

        entries = [self.url_result('hrti:%s' % video_id) for video_id in video_ids]

        return self.playlist_result(entries, category_id, display_id)


code:
from .common import InfoExtractor
from ..utils import (
    ExtractorError,
    traverse_obj,
    unified_timestamp,
)

class HSEShowBaseInfoExtractor(InfoExtractor):
    _GEO_COUNTRIES = ['DE']

    def _extract_redux_data(self, url, video_id):
        webpage = self._download_webpage(url, video_id)
        redux = self._html_search_regex(
            r'window\.__REDUX_DATA__\s*=\s*({.*});?', webpage, 'redux data')
        return self._parse_json(redux.replace('\n', ''), video_id)

    def _extract_formats_and_subtitles(self, sources, video_id):
        if not sources:
            raise ExtractorError('No video found', expected=True, video_id=video_id)
        formats, subtitles = [], {}
        for src in sources:
            if src['mimetype'] != 'application/x-mpegURL':
                continue
            fmts, subs = self._extract_m3u8_formats_and_subtitles(src['url'], video_id, ext='mp4')
            formats.extend(fmts)
            subtitles = self._merge_subtitles(subtitles, subs)
        self._sort_formats(formats)
        return formats, subtitles

class HSEShowIE(HSEShowBaseInfoExtractor):
    _VALID_URL = r'https?://(?:www\.)?hse\.de/dpl/c/tv-shows/(?P<id>[0-9]+)'
    _TESTS = [{
        'url': 'https://www.hse.de/dpl/c/tv-shows/505350',
        'info_dict': {
            'id': '505350',
            'ext': 'mp4',
            'title': 'Pfeffinger Mode & Accessoires',
            'timestamp': 1638810000,
            'upload_date': '20211206',
            'channel': 'HSE24',
            'uploader': 'Arina Pirayesh'
        },
        'params': {'skip_download': 'm3u8'},
    }]

    def _real_extract(self, url):
        video_id = self._match_id(url)
        json_data = self._extract_redux_data(url, video_id)
        formats, subtitles = self._extract_formats_and_subtitles(
            traverse_obj(json_data, ('tvShowPage', 'tvShowVideo', 'sources')), video_id)

        show = traverse_obj(json_data, ('tvShowPage', 'tvShow')) or {}
        return {
            'id': video_id,
            'title': show.get('title') or video_id,
            'formats': formats,
            'timestamp': unified_timestamp(f'{show.get("date")} {show.get("hour")}:00'),
            'thumbnail': traverse_obj(json_data, ('tvShowVideo', 'poster')),
            'channel': self._search_regex(
                r'tvShow \| ([A-Z0-9]+)_', show.get('actionFieldText') or '', video_id, fatal=False),
            'uploader': show.get('presenter'),
            'subtitles': subtitles,
        }

class HSEProductIE(HSEShowBaseInfoExtractor):
    _VALID_URL = r'https?://(?:www\.)?hse\.de/dpl/p/product/(?P<id>[0-9]+)'
    _TESTS = [{
        'url': 'https://www.hse.de/dpl/p/product/408630',
        'info_dict': {
            'id': '408630',
            'ext': 'mp4',
            'title': 'Hose im Ponte-Mix',
            'uploader': 'Judith Williams'
        },
        'params': {'skip_download': 'm3u8'},
    }]

    def _real_extract(self, url):
        video_id = self._match_id(url)
        json_data = self._extract_redux_data(url, video_id)
        video = traverse_obj(json_data, ('productContent', 'productContent', 'videos', 0)) or {}
        formats, subtitles = self._extract_formats_and_subtitles(video.get('sources'), video_id)

        return {
            'id': video_id,
            'title': traverse_obj(json_data, ('productDetail', 'product', 'name', 'short')) or video_id,
            'formats': formats,
            'subtitles': subtitles,
            'thumbnail': video.get('poster'),
            'uploader': traverse_obj(json_data, ('productDetail', 'product', 'brand', 'brandName')),
        }


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

class HuajiaoIE(InfoExtractor):
    IE_DESC = '花椒直播'
    _VALID_URL = r'https?://(?:www\.)?huajiao\.com/l/(?P<id>[0-9]+)'
    _TEST = {
        'url': 'http://www.huajiao.com/l/38941232',
        'md5': 'd08bf9ac98787d24d1e4c0283f2d372d',
        'info_dict': {
            'id': '38941232',
            'ext': 'mp4',
            'title': '#新人求关注#',
            'description': 're:.*',
            'duration': 2424.0,
            'thumbnail': r're:^https?://.*\.jpg$',
            'timestamp': 1475866459,
            'upload_date': '20161007',
            'uploader': 'Penny_余姿昀',
            'uploader_id': '75206005',
        }
    }

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

        feed_json = self._search_regex(
            r'var\s+feed\s*=\s*({.+})', webpage, 'feed json')
        feed = self._parse_json(feed_json, video_id)

        description = self._html_search_meta(
            'description', webpage, 'description', fatal=False)

        def get(section, field):
            return feed.get(section, {}).get(field)

        return {
            'id': video_id,
            'title': feed['feed']['formated_title'],
            'description': description,
            'duration': parse_duration(get('feed', 'duration')),
            'thumbnail': get('feed', 'image'),
            'timestamp': parse_iso8601(feed.get('creatime'), ' '),
            'uploader': get('author', 'nickname'),
            'uploader_id': get('author', 'uid'),
            'formats': self._extract_m3u8_formats(
                feed['feed']['m3u8'], video_id, 'mp4', 'm3u8_native'),
        }


code:
import re

from .common import InfoExtractor
from ..utils import (
    determine_ext,
    parse_duration,
    unified_strdate,
)

class HuffPostIE(InfoExtractor):
    IE_DESC = 'Huffington Post'
    _VALID_URL = r'''(?x)
        https?://(embed\.)?live\.huffingtonpost\.com/
        (?:
            r/segment/[^/]+/|
            HPLEmbedPlayer/\?segmentId=
        )
        (?P<id>[0-9a-f]+)'''
    _EMBED_REGEX = [r'<iframe[^>]+?src=(["\'])(?P<url>https?://embed\.live\.huffingtonpost\.com/.+?)\1']

    _TEST = {
        'url': 'http://live.huffingtonpost.com/r/segment/legalese-it/52dd3e4b02a7602131000677',
        'md5': '55f5e8981c1c80a64706a44b74833de8',
        'info_dict': {
            'id': '52dd3e4b02a7602131000677',
            'ext': 'mp4',
            'title': 'Legalese It! with @MikeSacksHP',
            'description': 'This week on Legalese It, Mike talks to David Bosco about his new book on the ICC, "Rough Justice," he also discusses the Virginia AG\'s historic stance on gay marriage, the execution of Edgar Tamayo, the ICC\'s delay of Kenya\'s President and more.  ',
            'duration': 1549,
            'upload_date': '20140124',
        },
        'params': {
            # m3u8 download
            'skip_download': True,
        },
        'expected_warnings': ['HTTP Error 404: Not Found'],
    }

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

        api_url = 'http://embed.live.huffingtonpost.com/api/segments/%s.json' % video_id
        data = self._download_json(api_url, video_id)['data']

        video_title = data['title']
        duration = parse_duration(data.get('running_time'))
        upload_date = unified_strdate(
            data.get('schedule', {}).get('starts_at') or data.get('segment_start_date_time'))
        description = data.get('description')

        thumbnails = []
        for url in filter(None, data['images'].values()):
            m = re.match(r'.*-([0-9]+x[0-9]+)\.', url)
            if not m:
                continue
            thumbnails.append({
                'url': url,
                'resolution': m.group(1),
            })

        formats = []
        sources = data.get('sources', {})
        live_sources = list(sources.get('live', {}).items()) + list(sources.get('live_again', {}).items())
        for key, url in live_sources:
            ext = determine_ext(url)
            if ext == 'm3u8':
                formats.extend(self._extract_m3u8_formats(
                    url, video_id, ext='mp4', m3u8_id='hls', fatal=False))
            elif ext == 'f4m':
                formats.extend(self._extract_f4m_formats(
                    url + '?hdcore=2.9.5', video_id, f4m_id='hds', fatal=False))
            else:
                formats.append({
                    'format': key,
                    'format_id': key.replace('/', '.'),
                    'ext': 'mp4',
                    'url': url,
                    'vcodec': 'none' if key.startswith('audio/') else None,
                })

        self._sort_formats(formats)

        return {
            'id': video_id,
            'title': video_title,
            'description': description,
            'formats': formats,
            'duration': duration,
            'upload_date': upload_date,
            'thumbnails': thumbnails,
        }