code:
import re

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

class HungamaIE(InfoExtractor):
    _VALID_URL = r'''(?x)
                    https?://
                        (?:www\.)?hungama\.com/
                        (?:
                            (?:video|movie)/[^/]+/|
                            tv-show/(?:[^/]+/){2}\d+/episode/[^/]+/
                        )
                        (?P<id>\d+)
                    '''
    _TESTS = [{
        'url': 'http://www.hungama.com/video/krishna-chants/39349649/',
        'md5': '687c5f1e9f832f3b59f44ed0eb1f120a',
        'info_dict': {
            'id': '39349649',
            'ext': 'mp4',
            'title': 'Krishna Chants',
            'description': 'Watch Krishna Chants video now. You can also watch other latest videos only at Hungama',
            'upload_date': '20180829',
            'duration': 264,
            'timestamp': 1535500800,
            'view_count': int,
            'thumbnail': 'https://images.hungama.com/c/1/0dc/2ca/39349649/39349649_700x394.jpg',
        }
    }, {
        'url': 'https://www.hungama.com/movie/kahaani-2/44129919/',
        'only_matching': True,
    }, {
        'url': 'https://www.hungama.com/tv-show/padded-ki-pushup/season-1/44139461/episode/ep-02-training-sasu-pathlaag-karing/44139503/',
        'only_matching': True,
    }]

    def _real_extract(self, url):
        video_id = self._match_id(url)
        video_json = self._download_json(
            'https://www.hungama.com/index.php', video_id,
            data=urlencode_postdata({'content_id': video_id}), headers={
                'Content-Type': 'application/x-www-form-urlencoded; charset=UTF-8',
                'X-Requested-With': 'XMLHttpRequest',
            }, query={
                'c': 'common',
                'm': 'get_video_mdn_url',
            })

        formats = self._extract_m3u8_formats(video_json['stream_url'], video_id, ext='mp4', m3u8_id='hls')
        self._sort_formats(formats)

        json_ld = self._search_json_ld(
            self._download_webpage(url, video_id, fatal=False) or '', video_id, fatal=False)

        return {
            **json_ld,
            'id': video_id,
            'formats': formats,
            'subtitles': {
                'en': [{
                    'url': video_json['sub_title'],
                    'ext': 'vtt',
                }]
            } if video_json.get('sub_title') else None,
        }

class HungamaSongIE(InfoExtractor):
    _VALID_URL = r'https?://(?:www\.)?hungama\.com/song/[^/]+/(?P<id>\d+)'
    _TEST = {
        'url': 'https://www.hungama.com/song/kitni-haseen-zindagi/2931166/',
        'md5': 'd4a6a05a394ad0453a9bea3ca00e6024',
        'info_dict': {
            'id': '2931166',
            'ext': 'mp3',
            'title': 'Lucky Ali - Kitni Haseen Zindagi',
            'track': 'Kitni Haseen Zindagi',
            'artist': 'Lucky Ali',
            'album': None,
            'release_year': 2000,
        }
    }

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

        data = self._download_json(
            'https://www.hungama.com/audio-player-data/track/%s' % audio_id,
            audio_id, query={'_country': 'IN'})[0]
        track = data['song_name']
        artist = data.get('singer_name')
        formats = []
        media_json = self._download_json(data.get('file') or data['preview_link'], audio_id)
        media_url = try_get(media_json, lambda x: x['response']['media_url'], str)
        media_type = try_get(media_json, lambda x: x['response']['type'], str)

        if media_url:
            formats.append({
                'url': media_url,
                'ext': media_type,
                'vcodec': 'none',
                'acodec': media_type,
            })

        title = '%s - %s' % (artist, track) if artist else track
        thumbnail = data.get('img_src') or data.get('album_image')

        return {
            'id': audio_id,
            'title': title,
            'thumbnail': thumbnail,
            'track': track,
            'artist': artist,
            'album': data.get('album_name') or None,
            'release_year': int_or_none(data.get('date')),
            'formats': formats,
        }

class HungamaAlbumPlaylistIE(InfoExtractor):
    _VALID_URL = r'https?://(?:www\.)?hungama\.com/(?:playlists|album)/[^/]+/(?P<id>\d+)'
    _TESTS = [{
        'url': 'https://www.hungama.com/album/bhuj-the-pride-of-india/69481490/',
        'playlist_mincount': 7,
        'info_dict': {
            'id': '69481490',
        },
    }, {
        'url': 'https://www.hungama.com/playlists/hindi-jan-to-june-2021/123063/',
        'playlist_mincount': 50,
        'info_dict': {
            'id': '123063',
        },
    }]

    def _real_extract(self, url):
        video_id = self._match_id(url)
        webpage = self._download_webpage(url, video_id)
        ptrn = r'<meta[^>]+?property=[\"\']?music:song:url[\"\']?[^>]+?content=[\"\']?([^\"\']+)'
        items = re.findall(ptrn, webpage)
        entries = [self.url_result(item, ie=HungamaSongIE.ie_key()) for item in items]
        return self.playlist_result(entries, video_id)


code:
import hashlib
import random

from ..compat import compat_urlparse, compat_b64decode

from ..utils import (
    ExtractorError,
    int_or_none,
    str_or_none,
    try_get,
    unescapeHTML,
    update_url_query,
)

from .common import InfoExtractor

class HuyaLiveIE(InfoExtractor):
    _VALID_URL = r'https?://(?:www\.|m\.)?huya\.com/(?P<id>[^/#?&]+)(?:\D|$)'
    IE_NAME = 'huya:live'
    IE_DESC = 'huya.com'
    TESTS = [{
        'url': 'https://www.huya.com/572329',
        'info_dict': {
            'id': '572329',
            'title': str,
            'description': str,
            'is_live': True,
            'view_count': int,
        },
        'params': {
            'skip_download': True,
        },
    }, {
        'url': 'https://www.huya.com/xiaoyugame',
        'only_matching': True
    }]

    _RESOLUTION = {
        '蓝光4M': {
            'width': 1920,
            'height': 1080,
        },
        '超清': {
            'width': 1280,
            'height': 720,
        },
        '流畅': {
            'width': 800,
            'height': 480
        }
    }

    def _real_extract(self, url):
        video_id = self._match_id(url)
        webpage = self._download_webpage(url, video_id=video_id)
        stream_data = self._search_json(r'stream:\s+', webpage, 'stream', video_id=video_id, default=None)
        room_info = try_get(stream_data, lambda x: x['data'][0]['gameLiveInfo'])
        if not room_info:
            raise ExtractorError('Can not extract the room info', expected=True)
        title = room_info.get('roomName') or room_info.get('introduction') or self._html_extract_title(webpage)
        screen_type = room_info.get('screenType')
        live_source_type = room_info.get('liveSourceType')
        stream_info_list = stream_data['data'][0]['gameStreamInfoList']
        if not stream_info_list:
            raise ExtractorError('Video is offline', expected=True)
        formats = []
        for stream_info in stream_info_list:
            stream_url = stream_info.get('sFlvUrl')
            if not stream_url:
                continue
            stream_name = stream_info.get('sStreamName')
            re_secret = not screen_type and live_source_type in (0, 8, 13)
            params = dict(compat_urlparse.parse_qsl(unescapeHTML(stream_info['sFlvAntiCode'])))
            fm, ss = '', ''
            if re_secret:
                fm, ss = self.encrypt(params, stream_info, stream_name)
            for si in stream_data.get('vMultiStreamInfo'):
                rate = si.get('iBitRate')
                if rate:
                    params['ratio'] = rate
                else:
                    params.pop('ratio', None)
                if re_secret:
                    params['wsSecret'] = hashlib.md5(
                        '_'.join([fm, params['u'], stream_name, ss, params['wsTime']]))
                formats.append({
                    'ext': stream_info.get('sFlvUrlSuffix'),
                    'format_id': str_or_none(stream_info.get('iLineIndex')),
                    'tbr': rate,
                    'url': update_url_query(f'{stream_url}/{stream_name}.{stream_info.get("sFlvUrlSuffix")}',
                                            query=params),
                    **self._RESOLUTION.get(si.get('sDisplayName'), {}),
                })

        self._sort_formats(formats)

        return {
            'id': video_id,
            'title': title,
            'formats': formats,
            'view_count': room_info.get('totalCount'),
            'thumbnail': room_info.get('screenshot'),
            'description': room_info.get('contentIntro'),
            'http_headers': {
                'Origin': 'https://www.huya.com',
                'Referer': 'https://www.huya.com/',
            },
        }

    def encrypt(self, params, stream_info, stream_name):
        ct = int_or_none(params.get('wsTime'), 16) + random.random()
        presenter_uid = stream_info['lPresenterUid']
        if not stream_name.startswith(str(presenter_uid)):
            uid = presenter_uid
        else:
            uid = int_or_none(ct % 1e7 * 1e6 % 0xffffffff)
        u1 = uid & 0xffffffff00000000
        u2 = uid & 0xffffffff
        u3 = uid & 0xffffff
        u = u1 | u2 >> 24 | u3 << 8
        params.update({
            'u': str_or_none(u),
            'seqid': str_or_none(int_or_none(ct * 1000) + uid),
            'ver': '1',
            'uuid': int_or_none(ct % 1e7 * 1e6 % 0xffffffff),
            't': '100',
        })
        fm = compat_b64decode(params['fm']).decode().split('_', 1)[0]
        ss = hashlib.md5('|'.join([params['seqid'], params['ctype'], params['t']]))
        return fm, ss

repo_name: Lesmiscore/ytdl


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

class HypemIE(InfoExtractor):
    _VALID_URL = r'https?://(?:www\.)?hypem\.com/track/(?P<id>[0-9a-z]{5})'
    _TEST = {
        'url': 'http://hypem.com/track/1v6ga/BODYWORK+-+TAME',
        'md5': 'b9cc91b5af8995e9f0c1cee04c575828',
        'info_dict': {
            'id': '1v6ga',
            'ext': 'mp3',
            'title': 'Tame',
            'uploader': 'BODYWORK',
            'timestamp': 1371810457,
            'upload_date': '20130621',
        }
    }

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

        response = self._download_webpage(url, track_id)

        track = self._parse_json(self._html_search_regex(
            r'(?s)<script\s+type="application/json"\s+id="displayList-data">(.+?)</script>',
            response, 'tracks'), track_id)['tracks'][0]

        track_id = track['id']
        title = track['song']

        final_url = self._download_json(
            'http://hypem.com/serve/source/%s/%s' % (track_id, track['key']),
            track_id, 'Downloading metadata', headers={
                'Content-Type': 'application/json'
            })['url']

        return {
            'id': track_id,
            'url': final_url,
            'ext': 'mp3',
            'title': title,
            'uploader': track.get('artist'),
            'duration': int_or_none(track.get('time')),
            'timestamp': int_or_none(track.get('ts')),
            'track': title,
        }


code:
import re

from .common import InfoExtractor
from ..utils import traverse_obj

class HytaleIE(InfoExtractor):
    _VALID_URL = r'https?://(?:www\.)?hytale\.com/news/\d+/\d+/(?P<id>[a-z0-9-]+)'
    _TESTS = [{
        'url': 'https://hytale.com/news/2021/07/summer-2021-development-update',
        'info_dict': {
            'id': 'summer-2021-development-update',
            'title': 'Summer 2021 Development Update',
        },
        'playlist_count': 4,
        'playlist': [{
            'md5': '0854ebe347d233ee19b86ab7b2ead610',
            'info_dict': {
                'id': 'ed51a2609d21bad6e14145c37c334999',
                'ext': 'mp4',
                'title': 'Avatar Personalization',
                'thumbnail': r're:https://videodelivery\.net/\w+/thumbnails/thumbnail\.jpg',
            }
        }]
    }, {
        'url': 'https://www.hytale.com/news/2019/11/hytale-graphics-update',
        'info_dict': {
            'id': 'hytale-graphics-update',
            'title': 'Hytale graphics update',
        },
        'playlist_count': 2,
    }]

    def _real_initialize(self):
        media_webpage = self._download_webpage(
            'https://hytale.com/media', None, note='Downloading list of media', fatal=False) or ''

        clips_json = traverse_obj(
            self._search_json(
                r'window\.__INITIAL_COMPONENTS_STATE__\s*=\s*\[',
                media_webpage, 'clips json', None),
            ('media', 'clips')) or []

        self._titles = {clip.get('src'): clip.get('caption') for clip in clips_json}

    def _real_extract(self, url):
        playlist_id = self._match_id(url)
        webpage = self._download_webpage(url, playlist_id)
        entries = [
            self.url_result(
                f'https://cloudflarestream.com/{video_hash}/manifest/video.mpd?parentOrigin=https%3A%2F%2Fhytale.com',
                title=self._titles.get(video_hash), url_transparent=True)
            for video_hash in re.findall(
                r'<stream\s+class\s*=\s*"ql-video\s+cf-stream"\s+src\s*=\s*"([a-f0-9]{32})"',
                webpage)
        ]

        return self.playlist_result(entries, playlist_id, self._og_search_title(webpage))


code:
import re

from .common import InfoExtractor
from ..utils import (
    clean_html,
    determine_ext,
    get_element_by_class,
    int_or_none,
    merge_dicts,
    parse_bitrate,
    parse_resolution,
    remove_end,
    str_or_none,
    url_or_none,
    urlencode_postdata,
)

class IcareusIE(InfoExtractor):
    _DOMAINS = '|'.join(map(re.escape, (
        'asahitv.fi',
        'helsinkikanava.fi',
        'hyvinvointitv.fi',
        'inez.fi',
        'permanto.fi',
        'suite.icareus.com',
        'videos.minifiddlers.org',
    )))
    _VALID_URL = rf'(?P<base_url>https?://(?:www\.)?(?:{_DOMAINS}))/[^?#]+/player/[^?#]+\?(?:[^#]+&)?(?:assetId|eventId)=(?P<id>\d+)'
    _TESTS = [{
        'url': 'https://www.helsinkikanava.fi/fi_FI/web/helsinkikanava/player/vod?assetId=68021894',
        'md5': 'ca0b62ffc814a5411dfa6349cf5adb8a',
        'info_dict': {
            'id': '68021894',
            'ext': 'mp4',
            'title': 'Perheiden parhaaksi',
            'description': 'md5:295785ea408e5ac00708766465cc1325',
            'thumbnail': 'https://www.helsinkikanava.fi/image/image_gallery?img_id=68022501',
            'upload_date': '20200924',
            'timestamp': 1600938300,
        },
    }, {  # Recorded livestream
        'url': 'https://www.helsinkikanava.fi/fi/web/helsinkikanava/player/event/view?eventId=76241489',
        'md5': '014327e69dfa7b949fcc861f6d162d6d',
        'info_dict': {
            'id': '76258304',
            'ext': 'mp4',
            'title': 'Helsingin kaupungin ja HUSin tiedotustilaisuus koronaepidemiatilanteesta 24.11.2020',
            'description': 'md5:3129d041c6fbbcdc7fe68d9a938fef1c',
            'thumbnail': 'https://icareus-suite.secure2.footprint.net/image/image_gallery?img_id=76288630',
            'upload_date': '20201124',
            'timestamp': 1606206600,
        },
    }, {  # Non-m3u8 stream
        'url': 'https://suite.icareus.com/fi/web/westend-indians/player/vod?assetId=47567389',
        'md5': '72fc04ee971bbedc44405cdf16c990b6',
        'info_dict': {
            'id': '47567389',
            'ext': 'mp4',
            'title': 'Omatoiminen harjoittelu - Laukominen',
            'description': '',
            'thumbnail': 'https://suite.icareus.com/image/image_gallery?img_id=47568162',
            'upload_date': '20200319',
            'timestamp': 1584658080,
        },
    }, {
        'url': 'https://asahitv.fi/fi/web/asahi/player/vod?assetId=89415818',
        'only_matching': True
    }, {
        'url': 'https://hyvinvointitv.fi/fi/web/hyvinvointitv/player/vod?assetId=89149730',
        'only_matching': True
    }, {
        'url': 'https://inez.fi/fi/web/inez-media/player/vod?assetId=71328822',
        'only_matching': True
    }, {
        'url': 'https://www.permanto.fi/fi/web/alfatv/player/vod?assetId=135497515',
        'only_matching': True
    }, {
        'url': 'https://videos.minifiddlers.org/web/international-minifiddlers/player/vod?assetId=1982759',
        'only_matching': True
    }]

    def _real_extract(self, url):
        base_url, temp_id = self._match_valid_url(url).groups()
        webpage = self._download_webpage(url, temp_id)

        video_id = self._search_regex(r"_icareus\['itemId'\]\s*=\s*'(\d+)'", webpage, 'video_id')
        organization_id = self._search_regex(r"_icareus\['organizationId'\]\s*=\s*'(\d+)'", webpage, 'organization_id')

        assets = self._download_json(
            self._search_regex(r'var\s+publishingServiceURL\s*=\s*"(http[^"]+)";', webpage, 'api_base'),
            video_id, data=urlencode_postdata({
                'version': '03',
                'action': 'getAssetPlaybackUrls',
                'organizationId': organization_id,
                'assetId': video_id,
                'token': self._search_regex(r"_icareus\['token'\]\s*=\s*'([a-f0-9]+)'", webpage, 'icareus_token'),
            }))

        subtitles = {
            remove_end(sdesc.split(' ')[0], ':'): [{'url': url_or_none(surl)}]
            for _, sdesc, surl in assets.get('subtitles') or []
        }

        formats = [{
            'format': item.get('name'),
            'format_id': 'audio',
            'vcodec': 'none',
            'url': url_or_none(item['url']),
            'tbr': int_or_none(self._search_regex(
                r'\((\d+)\s*k\)', item.get('name') or '', 'audio bitrate', default=None)),
        } for item in assets.get('audio_urls') or [] if url_or_none(item.get('url'))]

        for item in assets.get('urls') or []:
            video_url = url_or_none(item.get('url'))
            if video_url is None:
                continue
            ext = determine_ext(video_url)
            if ext == 'm3u8':
                fmts, subs = self._extract_m3u8_formats_and_subtitles(
                    video_url, video_id, 'mp4', m3u8_id='hls', fatal=False)
                formats.extend(fmts)
                self._merge_subtitles(subs, target=subtitles)
            else:
                fmt = item.get('name')
                formats.append({
                    'url': video_url,
                    'format': fmt,
                    'tbr': parse_bitrate(fmt),
                    'format_id': str_or_none(item.get('id')),
                    **parse_resolution(fmt),
                })

        info, token, live_title = self._search_json_ld(webpage, video_id, default={}), None, None
        if not info:
            token = self._search_regex(
                r'data\s*:\s*{action:"getAsset".*?token:\'([a-f0-9]+)\'}', webpage, 'token', default=None)
            if not token:
                live_title = get_element_by_class('unpublished-info-item future-event-title', webpage)

        if token:
            metadata = self._download_json(
                f'{base_url}/icareus-suite-api-portlet/publishing',
                video_id, fatal=False, data=urlencode_postdata({
                    'version': '03',
                    'action': 'getAsset',
                    'organizationId': organization_id,
                    'assetId': video_id,
                    'languageId': 'en_US',
                    'userId': '0',
                    'token': token,
                })) or {}
            info = {
                'title': metadata.get('name'),
                'description': metadata.get('description'),
                'timestamp': int_or_none(metadata.get('date'), scale=1000),
                'duration': int_or_none(metadata.get('duration')),
                'thumbnail': url_or_none(metadata.get('thumbnailMedium')),
            }
        elif live_title:  # Recorded livestream
            info = {
                'title': live_title,
                'description': get_element_by_class('unpublished-info-item future-event-description', webpage),
                'timestamp': int_or_none(self._search_regex(
                    r'var startEvent\s*=\s*(\d+);', webpage, 'uploadDate', fatal=False), scale=1000),
            }

        thumbnails = info.get('thumbnails') or [{
            'url': url_or_none(info.get('thumbnail') or assets.get('thumbnail'))
        }]

        self._sort_formats(formats)
        return merge_dicts({
            'id': video_id,
            'title': None,
            'formats': formats,
            'subtitles': subtitles,
            'description': clean_html(info.get('description')),
            'thumbnails': thumbnails if thumbnails[0]['url'] else None,
        }, info)


code:
from .common import InfoExtractor
from ..utils import ExtractorError, str_or_none, traverse_obj, unified_strdate

class IchinanaLiveIE(InfoExtractor):
    IE_NAME = '17live'
    _VALID_URL = r'https?://(?:www\.)?17\.live/(?:[^/]+/)*(?:live|profile/r)/(?P<id>\d+)'
    _TESTS = [{
        'url': 'https://17.live/live/3773096',
        'info_dict': {
            'id': '3773096',
            'title': '萠珈☕🤡🍫moka',
            'is_live': True,
            'uploader': '萠珈☕🤡🍫moka',
            'uploader_id': '3773096',
            'like_count': 366,
            'view_count': 18121,
            'timestamp': 1630569012,
        },
        'skip': 'running as of writing, but may be ended as of testing',
    }, {
        'note': 'nothing except language differs',
        'url': 'https://17.live/ja/live/3773096',
        'only_matching': True,
    }]

    @classmethod
    def suitable(cls, url):
        return not IchinanaLiveClipIE.suitable(url) and super(IchinanaLiveIE, cls).suitable(url)

    def _real_extract(self, url):
        video_id = self._match_id(url)
        url = 'https://17.live/live/%s' % video_id

        enter = self._download_json(
            'https://api-dsa.17app.co/api/v1/lives/%s/enter' % video_id, video_id,
            headers={'Referer': url}, fatal=False, expected_status=420,
            data=b'\0')
        if enter and enter.get('message') == 'ended':
            raise ExtractorError('This live has ended.', expected=True)

        view_data = self._download_json(
            'https://api-dsa.17app.co/api/v1/lives/%s' % video_id, video_id,
            headers={'Referer': url})

        uploader = traverse_obj(
            view_data, ('userInfo', 'displayName'), ('userInfo', 'openID'))

        video_urls = view_data.get('rtmpUrls')
        if not video_urls:
            raise ExtractorError('unable to extract live URL information')
        formats = []
        for (name, value) in video_urls[0].items():
            if not isinstance(value, str):
                continue
            if not value.startswith('http'):
                continue
            quality = -1
            if 'web' in name:
                quality -= 1
            if 'High' in name:
                quality += 4
            if 'Low' in name:
                quality -= 2
            formats.append({
                'format_id': name,
                'url': value,
                'quality': quality,
                'http_headers': {'Referer': url},
                'ext': 'flv',
                'vcodec': 'h264',
                'acodec': 'aac',
            })

        self._sort_formats(formats)

        return {
            'id': video_id,
            'title': uploader or video_id,
            'formats': formats,
            'is_live': True,
            'uploader': uploader,
            'uploader_id': video_id,
            'like_count': view_data.get('receivedLikeCount'),
            'view_count': view_data.get('viewerCount'),
            'thumbnail': view_data.get('coverPhoto'),
            'description': view_data.get('caption'),
            'timestamp': view_data.get('beginTime'),
        }

class IchinanaLiveClipIE(InfoExtractor):
    IE_NAME = '17live:clip'
    _VALID_URL = r'https?://(?:www\.)?17\.live/(?:[^/]+/)*profile/r/(?P<uploader_id>\d+)/clip/(?P<id>[^/]+)'
    _TESTS = [{
        'url': 'https://17.live/profile/r/1789280/clip/1bHQSK8KUieruFXaCH4A4upCzlN',
        'info_dict': {
            'id': '1bHQSK8KUieruFXaCH4A4upCzlN',
            'title': 'マチコ先生🦋Class💋',
            'description': 'マチ戦隊　第一次　バスターコール\n総額200万coin！\n動画制作@うぉーかー🌱Walker🎫',
            'uploader_id': '1789280',
        },
    }, {
        'url': 'https://17.live/ja/profile/r/1789280/clip/1bHQSK8KUieruFXaCH4A4upCzlN',
        'only_matching': True,
    }]

    def _real_extract(self, url):
        uploader_id, video_id = self._match_valid_url(url).groups()
        url = 'https://17.live/profile/r/%s/clip/%s' % (uploader_id, video_id)

        view_data = self._download_json(
            'https://api-dsa.17app.co/api/v1/clips/%s' % video_id, video_id,
            headers={'Referer': url})

        uploader = traverse_obj(
            view_data, ('userInfo', 'displayName'), ('userInfo', 'name'))

        formats = []
        if view_data.get('videoURL'):
            formats.append({
                'id': 'video',
                'url': view_data['videoURL'],
                'quality': -1,
            })
        if view_data.get('transcodeURL'):
            formats.append({
                'id': 'transcode',
                'url': view_data['transcodeURL'],
                'quality': -1,
            })
        if view_data.get('srcVideoURL'):
            # highest quality
            formats.append({
                'id': 'srcVideo',
                'url': view_data['srcVideoURL'],
                'quality': 1,
            })

        for fmt in formats:
            fmt.update({
                'ext': 'mp4',
                'protocol': 'https',
                'vcodec': 'h264',
                'acodec': 'aac',
                'http_headers': {'Referer': url},
            })

        self._sort_formats(formats)

        return {
            'id': video_id,
            'title': uploader or video_id,
            'formats': formats,
            'uploader': uploader,
            'uploader_id': uploader_id,
            'like_count': view_data.get('likeCount'),
            'view_count': view_data.get('viewCount'),
            'thumbnail': view_data.get('imageURL'),
            'duration': view_data.get('duration'),
            'description': view_data.get('caption'),
            'upload_date': unified_strdate(str_or_none(view_data.get('createdAt'))),
        }


code:
import re

from .common import InfoExtractor
from ..compat import (
    compat_parse_qs,
    compat_urllib_parse_urlparse,
)
from ..utils import (
    HEADRequest,
    determine_ext,
    int_or_none,
    parse_iso8601,
    strip_or_none,
    try_get,
)

class IGNBaseIE(InfoExtractor):
    def _call_api(self, slug):
        return self._download_json(
            'http://apis.ign.com/{0}/v3/{0}s/slug/{1}'.format(self._PAGE_TYPE, slug), slug)

class IGNIE(IGNBaseIE):
    """
    Extractor for some of the IGN sites, like www.ign.com, es.ign.com de.ign.com.
    Some videos of it.ign.com are also supported
    """

    _VALID_URL = r'https?://(?:.+?\.ign|www\.pcmag)\.com/videos/(?:\d{4}/\d{2}/\d{2}/)?(?P<id>[^/?&#]+)'
    IE_NAME = 'ign.com'
    _PAGE_TYPE = 'video'

    _TESTS = [{
        'url': 'http://www.ign.com/videos/2013/06/05/the-last-of-us-review',
        'md5': 'd2e1586d9987d40fad7867bf96a018ea',
        'info_dict': {
            'id': '8f862beef863986b2785559b9e1aa599',
            'ext': 'mp4',
            'title': 'The Last of Us Review',
            'description': 'md5:c8946d4260a4d43a00d5ae8ed998870c',
            'timestamp': 1370440800,
            'upload_date': '20130605',
            'tags': 'count:9',
        }
    }, {
        'url': 'http://www.pcmag.com/videos/2015/01/06/010615-whats-new-now-is-gogo-snooping-on-your-data',
        'md5': 'f1581a6fe8c5121be5b807684aeac3f6',
        'info_dict': {
            'id': 'ee10d774b508c9b8ec07e763b9125b91',
            'ext': 'mp4',
            'title': 'What\'s New Now: Is GoGo Snooping on Your Data?',
            'description': 'md5:817a20299de610bd56f13175386da6fa',
            'timestamp': 1420571160,
            'upload_date': '20150106',
            'tags': 'count:4',
        }
    }, {
        'url': 'https://www.ign.com/videos/is-a-resident-evil-4-remake-on-the-way-ign-daily-fix',
        'only_matching': True,
    }]

    def _real_extract(self, url):
        display_id = self._match_id(url)
        video = self._call_api(display_id)
        video_id = video['videoId']
        metadata = video['metadata']
        title = metadata.get('longTitle') or metadata.get('title') or metadata['name']

        formats = []
        refs = video.get('refs') or {}

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

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

        for asset in (video.get('assets') or []):
            asset_url = asset.get('url')
            if not asset_url:
                continue
            formats.append({
                'url': asset_url,
                'tbr': int_or_none(asset.get('bitrate'), 1000),
                'fps': int_or_none(asset.get('frame_rate')),
                'height': int_or_none(asset.get('height')),
                'width': int_or_none(asset.get('width')),
            })

        mezzanine_url = try_get(video, lambda x: x['system']['mezzanineUrl'])
        if mezzanine_url:
            formats.append({
                'ext': determine_ext(mezzanine_url, 'mp4'),
                'format_id': 'mezzanine',
                'quality': 1,
                'url': mezzanine_url,
            })

        self._sort_formats(formats)

        thumbnails = []
        for thumbnail in (video.get('thumbnails') or []):
            thumbnail_url = thumbnail.get('url')
            if not thumbnail_url:
                continue
            thumbnails.append({
                'url': thumbnail_url,
            })

        tags = []
        for tag in (video.get('tags') or []):
            display_name = tag.get('displayName')
            if not display_name:
                continue
            tags.append(display_name)

        return {
            'id': video_id,
            'title': title,
            'description': strip_or_none(metadata.get('description')),
            'timestamp': parse_iso8601(metadata.get('publishDate')),
            'duration': int_or_none(metadata.get('duration')),
            'display_id': display_id,
            'thumbnails': thumbnails,
            'formats': formats,
            'tags': tags,
        }

class IGNVideoIE(InfoExtractor):
    _VALID_URL = r'https?://.+?\.ign\.com/(?:[a-z]{2}/)?[^/]+/(?P<id>\d+)/(?:video|trailer)/'
    _TESTS = [{
        'url': 'http://me.ign.com/en/videos/112203/video/how-hitman-aims-to-be-different-than-every-other-s',
        'md5': 'dd9aca7ed2657c4e118d8b261e5e9de1',
        'info_dict': {
            'id': 'e9be7ea899a9bbfc0674accc22a36cc8',
            'ext': 'mp4',
            'title': 'How Hitman Aims to Be Different Than Every Other Stealth Game - NYCC 2015',
            'description': 'Taking out assassination targets in Hitman has never been more stylish.',
            'timestamp': 1444665600,
            'upload_date': '20151012',
        }
    }, {
        'url': 'http://me.ign.com/ar/angry-birds-2/106533/video/lrd-ldyy-lwl-lfylm-angry-birds',
        'only_matching': True,
    }, {
        # Youtube embed
        'url': 'https://me.ign.com/ar/ratchet-clank-rift-apart/144327/trailer/embed',
        'only_matching': True,
    }, {
        # Twitter embed
        'url': 'http://adria.ign.com/sherlock-season-4/9687/trailer/embed',
        'only_matching': True,
    }, {
        # Vimeo embed
        'url': 'https://kr.ign.com/bic-2018/3307/trailer/embed',
        'only_matching': True,
    }]

    def _real_extract(self, url):
        video_id = self._match_id(url)
        req = HEADRequest(url.rsplit('/', 1)[0] + '/embed')
        url = self._request_webpage(req, video_id).geturl()
        ign_url = compat_parse_qs(
            compat_urllib_parse_urlparse(url).query).get('url', [None])[0]
        if ign_url:
            return self.url_result(ign_url, IGNIE.ie_key())
        return self.url_result(url)

class IGNArticleIE(IGNBaseIE):
    _VALID_URL = r'https?://.+?\.ign\.com/(?:articles(?:/\d{4}/\d{2}/\d{2})?|(?:[a-z]{2}/)?feature/\d+)/(?P<id>[^/?&#]+)'
    _PAGE_TYPE = 'article'
    _TESTS = [{
        'url': 'http://me.ign.com/en/feature/15775/100-little-things-in-gta-5-that-will-blow-your-mind',
        'info_dict': {
            'id': '524497489e4e8ff5848ece34',
            'title': '100 Little Things in GTA 5 That Will Blow Your Mind',
        },
        'playlist': [
            {
                'info_dict': {
                    'id': '5ebbd138523268b93c9141af17bec937',
                    'ext': 'mp4',
                    'title': 'GTA 5 Video Review',
                    'description': 'Rockstar drops the mic on this generation of games. Watch our review of the masterly Grand Theft Auto V.',
                    'timestamp': 1379339880,
                    'upload_date': '20130916',
                },
            },
            {
                'info_dict': {
                    'id': '638672ee848ae4ff108df2a296418ee2',
                    'ext': 'mp4',
                    'title': '26 Twisted Moments from GTA 5 in Slow Motion',
                    'description': 'The twisted beauty of GTA 5 in stunning slow motion.',
                    'timestamp': 1386878820,
                    'upload_date': '20131212',
                },
            },
        ],
        'params': {
            'playlist_items': '2-3',
            'skip_download': True,
        },
    }, {
        'url': 'http://www.ign.com/articles/2014/08/15/rewind-theater-wild-trailer-gamescom-2014?watch',
        'info_dict': {
            'id': '53ee806780a81ec46e0790f8',
            'title': 'Rewind Theater - Wild Trailer Gamescom 2014',
        },
        'playlist_count': 2,
    }, {
        # videoId pattern
        'url': 'http://www.ign.com/articles/2017/06/08/new-ducktales-short-donalds-birthday-doesnt-go-as-planned',
        'only_matching': True,
    }, {
        # Youtube embed
        'url': 'https://www.ign.com/articles/2021-mvp-named-in-puppy-bowl-xvii',
        'only_matching': True,
    }, {
        # IMDB embed
        'url': 'https://www.ign.com/articles/2014/08/07/sons-of-anarchy-final-season-trailer',
        'only_matching': True,
    }, {
        # Facebook embed
        'url': 'https://www.ign.com/articles/2017/09/20/marvels-the-punisher-watch-the-new-trailer-for-the-netflix-series',
        'only_matching': True,
    }, {
        # Brightcove embed
        'url': 'https://www.ign.com/articles/2016/01/16/supergirl-goes-flying-with-martian-manhunter-in-new-clip',
        'only_matching': True,
    }]

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

        def entries():
            media_url = try_get(article, lambda x: x['mediaRelations'][0]['media']['metadata']['url'])
            if media_url:
                yield self.url_result(media_url, IGNIE.ie_key())
            for content in (article.get('content') or []):
                for video_url in re.findall(r'(?:\[(?:ignvideo\s+url|youtube\s+clip_id)|<iframe[^>]+src)="([^"]+)"', content):
                    yield self.url_result(video_url)

        return self.playlist_result(
            entries(), article.get('articleId'),
            strip_or_none(try_get(article, lambda x: x['metadata']['headline'])))


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

class IHeartRadioBaseIE(InfoExtractor):
    def _call_api(self, path, video_id, fatal=True, query=None):
        return self._download_json(
            'https://api.iheart.com/api/v3/podcast/' + path,
            video_id, fatal=fatal, query=query)

    def _extract_episode(self, episode):
        return {
            'thumbnail': episode.get('imageUrl'),
            'description': clean_html(episode.get('description')),
            'timestamp': int_or_none(episode.get('startDate'), 1000),
            'duration': int_or_none(episode.get('duration')),
        }

class IHeartRadioIE(IHeartRadioBaseIE):
    IENAME = 'iheartradio'
    _VALID_URL = r'(?:https?://(?:www\.)?iheart\.com/podcast/[^/]+/episode/(?P<display_id>[^/?&#]+)-|iheartradio:)(?P<id>\d+)'
    _TEST = {
        'url': 'https://www.iheart.com/podcast/105-behind-the-bastards-29236323/episode/part-one-alexander-lukashenko-the-dictator-70346499/?embed=true',
        'md5': 'c8609c92c8688dcb69d8541042b8abca',
        'info_dict': {
            'id': '70346499',
            'ext': 'mp3',
            'title': 'Part One: Alexander Lukashenko: The Dictator of Belarus',
            'description': 'md5:96cc7297b3a5a9ebae28643801c96fae',
            'timestamp': 1597741200,
            'upload_date': '20200818',
        }
    }

    def _real_extract(self, url):
        episode_id = self._match_id(url)
        episode = self._call_api(
            'episodes/' + episode_id, episode_id)['episode']
        info = self._extract_episode(episode)
        info.update({
            'id': episode_id,
            'title': episode['title'],
            'url': clean_podcast_url(episode['mediaUrl']),
        })
        return info

class IHeartRadioPodcastIE(IHeartRadioBaseIE):
    IE_NAME = 'iheartradio:podcast'
    _VALID_URL = r'https?://(?:www\.)?iheart(?:podcastnetwork)?\.com/podcast/[^/?&#]+-(?P<id>\d+)/?(?:[?#&]|$)'
    _TESTS = [{
        'url': 'https://www.iheart.com/podcast/1119-it-could-happen-here-30717896/',
        'info_dict': {
            'id': '30717896',
            'title': 'It Could Happen Here',
            'description': 'md5:5842117412a967eb0b01f8088eb663e2',
        },
        'playlist_mincount': 11,
    }, {
        'url': 'https://www.iheartpodcastnetwork.com/podcast/105-stuff-you-should-know-26940277',
        'only_matching': True,
    }]

    def _real_extract(self, url):
        podcast_id = self._match_id(url)
        path = 'podcasts/' + podcast_id
        episodes = self._call_api(
            path + '/episodes', podcast_id, query={'limit': 1000000000})['data']

        entries = []
        for episode in episodes:
            episode_id = str_or_none(episode.get('id'))
            if not episode_id:
                continue
            info = self._extract_episode(episode)
            info.update({
                '_type': 'url',
                'id': episode_id,
                'title': episode.get('title'),
                'url': 'iheartradio:' + episode_id,
                'ie_key': IHeartRadioIE.ie_key(),
            })
            entries.append(info)

        podcast = self._call_api(path, podcast_id, False) or {}

        return self.playlist_result(
            entries, podcast_id, podcast.get('title'), podcast.get('description'))


code:
import base64
import json
import re

from .common import InfoExtractor
from ..utils import (
    determine_ext,
    int_or_none,
    mimetype2ext,
    qualities,
    traverse_obj,
    try_get,
    url_or_none,
)

class ImdbIE(InfoExtractor):
    IE_NAME = 'imdb'
    IE_DESC = 'Internet Movie Database trailers'
    _VALID_URL = r'https?://(?:www|m)\.imdb\.com/(?:video|title|list).*?[/-]vi(?P<id>\d+)'

    _TESTS = [{
        'url': 'http://www.imdb.com/video/imdb/vi2524815897',
        'info_dict': {
            'id': '2524815897',
            'ext': 'mp4',
            'title': 'No. 2',
            'description': 'md5:87bd0bdc61e351f21f20d2d7441cb4e7',
            'duration': 152,
            'thumbnail': r're:^https?://.+\.jpg',
        }
    }, {
        'url': 'https://www.imdb.com/video/vi3516832537',
        'info_dict': {
            'id': '3516832537',
            'ext': 'mp4',
            'title': 'Paul: U.S. Trailer #1',
            'description': 'md5:17fcc4fe11ec29b4399be9d4c5ef126c',
            'duration': 153,
            'thumbnail': r're:^https?://.+\.jpg',
        }
    }, {
        'url': 'http://www.imdb.com/video/_/vi2524815897',
        'only_matching': True,
    }, {
        'url': 'http://www.imdb.com/title/tt1667889/?ref_=ext_shr_eml_vi#lb-vi2524815897',
        'only_matching': True,
    }, {
        'url': 'http://www.imdb.com/title/tt1667889/#lb-vi2524815897',
        'only_matching': True,
    }, {
        'url': 'http://www.imdb.com/videoplayer/vi1562949145',
        'only_matching': True,
    }, {
        'url': 'http://www.imdb.com/title/tt4218696/videoplayer/vi2608641561',
        'only_matching': True,
    }, {
        'url': 'https://www.imdb.com/list/ls009921623/videoplayer/vi260482329',
        'only_matching': True,
    }]

    def _real_extract(self, url):
        video_id = self._match_id(url)
        webpage = self._download_webpage(f'https://www.imdb.com/video/vi{video_id}', video_id)
        info = self._search_nextjs_data(webpage, video_id)
        video_info = traverse_obj(info, ('props', 'pageProps', 'videoPlaybackData', 'video'), default={})
        title = (traverse_obj(video_info, ('name', 'value'), ('primaryTitle', 'titleText', 'text'))
                 or self._html_search_meta(('og:title', 'twitter:title'), webpage, default=None)
                 or self._html_extract_title(webpage))
        data = video_info.get('playbackURLs') or try_get(self._download_json(
            'https://www.imdb.com/ve/data/VIDEO_PLAYBACK_DATA', video_id,
            query={
                'key': base64.b64encode(json.dumps({
                    'type': 'VIDEO_PLAYER',
                    'subType': 'FORCE_LEGACY',
                    'id': 'vi%s' % video_id,
                }).encode()).decode(),
            }), lambda x: x[0]['videoLegacyEncodings'])
        quality = qualities(('SD', '480p', '720p', '1080p'))
        formats, subtitles = [], {}
        for encoding in data:
            if not encoding or not isinstance(encoding, dict):
                continue
            video_url = url_or_none(encoding.get('url'))
            if not video_url:
                continue
            ext = mimetype2ext(encoding.get(
                'mimeType')) or determine_ext(video_url)
            if ext == 'm3u8':
                fmts, subs = self._extract_m3u8_formats_and_subtitles(
                    video_url, video_id, 'mp4', entry_protocol='m3u8_native',
                    preference=1, m3u8_id='hls', fatal=False)
                subtitles = self._merge_subtitles(subtitles, subs)
                formats.extend(fmts)
                continue
            format_id = traverse_obj(encoding, ('displayName', 'value'), 'definition')
            formats.append({
                'format_id': format_id,
                'url': video_url,
                'ext': ext,
                'quality': quality(format_id),
            })
        self._sort_formats(formats)

        return {
            'id': video_id,
            'title': title,
            'alt_title': info.get('videoSubTitle'),
            'formats': formats,
            'description': try_get(video_info, lambda x: x['description']['value']),
            'thumbnail': url_or_none(try_get(video_info, lambda x: x['thumbnail']['url'])),
            'duration': int_or_none(try_get(video_info, lambda x: x['runtime']['value'])),
            'subtitles': subtitles,
        }

class ImdbListIE(InfoExtractor):
    IE_NAME = 'imdb:list'
    IE_DESC = 'Internet Movie Database lists'
    _VALID_URL = r'https?://(?:www\.)?imdb\.com/list/ls(?P<id>\d{9})(?!/videoplayer/vi\d+)'
    _TEST = {
        'url': 'https://www.imdb.com/list/ls009921623/',
        'info_dict': {
            'id': '009921623',
            'title': 'The Bourne Legacy',
            'description': 'A list of trailers, clips, and more from The Bourne Legacy, starring Jeremy Renner and Rachel Weisz.',
        },
        'playlist_count': 8,
    }

    def _real_extract(self, url):
        list_id = self._match_id(url)
        webpage = self._download_webpage(url, list_id)
        entries = [
            self.url_result('http://www.imdb.com' + m, 'Imdb')
            for m in re.findall(r'href="(/list/ls%s/videoplayer/vi[^"]+)"' % list_id, webpage)]

        list_title = self._html_search_regex(
            r'
]+class="[^"]*header[^"]*"[^>]*>(.*?)
',
            webpage, 'list title')
        list_description = self._html_search_regex(
            r'
]+class="[^"]*list-description[^"]*"[^>]*>
(.*?)
',
            webpage, 'list description')

        return self.playlist_result(entries, list_id, list_title, list_description)


code:
import json

from .common import InfoExtractor
from ..compat import compat_HTTPError
from ..utils import (
    ExtractorError,
    int_or_none,
    str_or_none,
    try_get,
)

class ImgGamingBaseIE(InfoExtractor):
    _API_BASE = 'https://dce-frontoffice.imggaming.com/api/v2/'
    _API_KEY = '857a1e5d-e35e-4fdf-805b-a87b6f8364bf'
    _HEADERS = None
    _MANIFEST_HEADERS = {'Accept-Encoding': 'identity'}
    _REALM = None
    _VALID_URL_TEMPL = r'https?://(?P<domain>%s)/(?P<type>live|playlist|video)/(?P<id>\d+)(?:\?.*?\bplaylistId=(?P<playlist_id>\d+))?'

    def _initialize_pre_login(self):
        self._HEADERS = {
            'Realm': 'dce.' + self._REALM,
            'x-api-key': self._API_KEY,
        }

    def _perform_login(self, username, password):
        p_headers = self._HEADERS.copy()
        p_headers['Content-Type'] = 'application/json'
        self._HEADERS['Authorization'] = 'Bearer ' + self._download_json(
            self._API_BASE + 'login',
            None, 'Logging in', data=json.dumps({
                'id': username,
                'secret': password,
            }).encode(), headers=p_headers)['authorisationToken']

    def _real_initialize(self):
        if not self._HEADERS.get('Authorization'):
            self.raise_login_required(method='password')

    def _call_api(self, path, media_id):
        return self._download_json(
            self._API_BASE + path + media_id, media_id, headers=self._HEADERS)

    def _extract_dve_api_url(self, media_id, media_type):
        stream_path = 'stream'
        if media_type == 'video':
            stream_path += '/vod/'
        else:
            stream_path += '?eventId='
        try:
            return self._call_api(
                stream_path, media_id)['playerUrlCallback']
        except ExtractorError as e:
            if isinstance(e.cause, compat_HTTPError) and e.cause.code == 403:
                raise ExtractorError(
                    self._parse_json(e.cause.read().decode(), media_id)['messages'][0],
                    expected=True)
            raise

    def _real_extract(self, url):
        domain, media_type, media_id, playlist_id = self._match_valid_url(url).groups()

        if playlist_id:
            if self._yes_playlist(playlist_id, media_id):
                media_type, media_id = 'playlist', playlist_id

        if media_type == 'playlist':
            playlist = self._call_api('vod/playlist/', media_id)
            entries = []
            for video in try_get(playlist, lambda x: x['videos']['vods']) or []:
                video_id = str_or_none(video.get('id'))
                if not video_id:
                    continue
                entries.append(self.url_result(
                    'https://%s/video/%s' % (domain, video_id),
                    self.ie_key(), video_id))
            return self.playlist_result(
                entries, media_id, playlist.get('title'),
                playlist.get('description'))

        dve_api_url = self._extract_dve_api_url(media_id, media_type)
        video_data = self._download_json(dve_api_url, media_id)
        is_live = media_type == 'live'
        if is_live:
            title = self._call_api('event/', media_id)['title']
        else:
            title = video_data['name']

        formats = []
        for proto in ('hls', 'dash'):
            media_url = video_data.get(proto + 'Url') or try_get(video_data, lambda x: x[proto]['url'])
            if not media_url:
                continue
            if proto == 'hls':
                m3u8_formats = self._extract_m3u8_formats(
                    media_url, media_id, 'mp4', live=is_live,
                    m3u8_id='hls', fatal=False, headers=self._MANIFEST_HEADERS)
                for f in m3u8_formats:
                    f.setdefault('http_headers', {}).update(self._MANIFEST_HEADERS)
                    formats.append(f)
            else:
                formats.extend(self._extract_mpd_formats(
                    media_url, media_id, mpd_id='dash', fatal=False,
                    headers=self._MANIFEST_HEADERS))
        self._sort_formats(formats)

        subtitles = {}
        for subtitle in video_data.get('subtitles', []):
            subtitle_url = subtitle.get('url')
            if not subtitle_url:
                continue
            subtitles.setdefault(subtitle.get('lang', 'en_US'), []).append({
                'url': subtitle_url,
            })

        return {
            'id': media_id,
            'title': title,
            'formats': formats,
            'thumbnail': video_data.get('thumbnailUrl'),
            'description': video_data.get('description'),
            'duration': int_or_none(video_data.get('duration')),
            'tags': video_data.get('tags'),
            'is_live': is_live,
            'subtitles': subtitles,
        }


code:
import re

from .common import InfoExtractor
from ..utils import (
    int_or_none,
    js_to_json,
    mimetype2ext,
    ExtractorError,
)

class ImgurIE(InfoExtractor):
    _VALID_URL = r'https?://(?:i\.)?imgur\.com/(?!(?:a|gallery|(?:t(?:opic)?|r)/[^/]+)/)(?P<id>[a-zA-Z0-9]+)'

    _TESTS = [{
        'url': 'https://i.imgur.com/A61SaA1.gifv',
        'info_dict': {
            'id': 'A61SaA1',
            'ext': 'mp4',
            'title': 're:Imgur GIF$|MRW gifv is up and running without any bugs$',
        },
    }, {
        'url': 'https://imgur.com/A61SaA1',
        'only_matching': True,
    }, {
        'url': 'https://i.imgur.com/crGpqCV.mp4',
        'only_matching': True,
    }, {
        # no title
        'url': 'https://i.imgur.com/jxBXAMC.gifv',
        'only_matching': True,
    }]

    def _real_extract(self, url):
        video_id = self._match_id(url)
        webpage = self._download_webpage(
            'https://i.imgur.com/{id}.gifv'.format(id=video_id), video_id)

        width = int_or_none(self._og_search_property(
            'video:width', webpage, default=None))
        height = int_or_none(self._og_search_property(
            'video:height', webpage, default=None))

        video_elements = self._search_regex(
            r'(?s)
(.*?)
',
            webpage, 'video elements', default=None)
        if not video_elements:
            raise ExtractorError(
                'No sources found for video %s. Maybe an image?' % video_id,
                expected=True)

        formats = []
        for m in re.finditer(r'<source\s+src="(?P<src>[^"]+)"\s+type="(?P<type>[^"]+)"', video_elements):
            formats.append({
                'format_id': m.group('type').partition('/')[2],
                'url': self._proto_relative_url(m.group('src')),
                'ext': mimetype2ext(m.group('type')),
                'width': width,
                'height': height,
                'http_headers': {
                    'User-Agent': 'yt-dlp (like wget)',
                },
            })

        gif_json = self._search_regex(
            r'(?s)var\s+videoItem\s*=\s*(\{.*?\})',
            webpage, 'GIF code', fatal=False)
        if gif_json:
            gifd = self._parse_json(
                gif_json, video_id, transform_source=js_to_json)
            formats.append({
                'format_id': 'gif',
                'preference': -10,  # gifs are worse than videos
                'width': width,
                'height': height,
                'ext': 'gif',
                'acodec': 'none',
                'vcodec': 'gif',
                'container': 'gif',
                'url': self._proto_relative_url(gifd['gifUrl']),
                'filesize': gifd.get('size'),
                'http_headers': {
                    'User-Agent': 'yt-dlp (like wget)',
                },
            })

        self._sort_formats(formats)

        return {
            'id': video_id,
            'formats': formats,
            'title': self._og_search_title(webpage, default=video_id),
        }

class ImgurGalleryIE(InfoExtractor):
    IE_NAME = 'imgur:gallery'
    _VALID_URL = r'https?://(?:i\.)?imgur\.com/(?:gallery|(?:t(?:opic)?|r)/[^/]+)/(?P<id>[a-zA-Z0-9]+)'

    _TESTS = [{
        'url': 'http://imgur.com/gallery/Q95ko',
        'info_dict': {
            'id': 'Q95ko',
            'title': 'Adding faces make every GIF better',
        },
        'playlist_count': 25,
    }, {
        'url': 'http://imgur.com/topic/Aww/ll5Vk',
        'only_matching': True,
    }, {
        'url': 'https://imgur.com/gallery/YcAQlkx',
        'info_dict': {
            'id': 'YcAQlkx',
            'ext': 'mp4',
            'title': 'Classic Steve Carell gif...cracks me up everytime....damn the repost downvotes....',
        }
    }, {
        'url': 'http://imgur.com/topic/Funny/N8rOudd',
        'only_matching': True,
    }, {
        'url': 'http://imgur.com/r/aww/VQcQPhM',
        'only_matching': True,
    }]

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

        data = self._download_json(
            'https://imgur.com/gallery/%s.json' % gallery_id,
            gallery_id)['data']['image']

        if data.get('is_album'):
            entries = [
                self.url_result('http://imgur.com/%s' % image['hash'], ImgurIE.ie_key(), image['hash'])
                for image in data['album_images']['images'] if image.get('hash')]
            return self.playlist_result(entries, gallery_id, data.get('title'), data.get('description'))

        return self.url_result('http://imgur.com/%s' % gallery_id, ImgurIE.ie_key(), gallery_id)

class ImgurAlbumIE(ImgurGalleryIE):
    IE_NAME = 'imgur:album'
    _VALID_URL = r'https?://(?:i\.)?imgur\.com/a/(?P<id>[a-zA-Z0-9]+)'

    _TESTS = [{
        'url': 'http://imgur.com/a/j6Orj',
        'info_dict': {
            'id': 'j6Orj',
            'title': 'A Literary Analysis of "Star Wars: The Force Awakens"',
        },
        'playlist_count': 12,
    }]


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

class InaIE(InfoExtractor):
    _VALID_URL = r'https?://(?:(?:www|m)\.)?ina\.fr/(?:[^?#]+/)(?P<id>[\w-]+)'
    _TESTS = [{
        'url': 'https://www.ina.fr/video/I12055569/francois-hollande-je-crois-que-c-est-clair-video.html',
        'md5': 'c5a09e5cb5604ed10709f06e7a377dda',
        'info_dict': {
            'id': 'I12055569',
            'ext': 'mp4',
            'title': 'François Hollande "Je crois que c\'est clair"',
            'description': 'md5:19f61e2b4844ed4bb2e3df9ab9f527ff',
            'upload_date': '20070712',
            'thumbnail': 'https://cdn-hub.ina.fr/notice/690x517/3c4/I12055569.jpeg',
        }
    }, {
        'url': 'https://www.ina.fr/video/S806544_001/don-d-organes-des-avancees-mais-d-importants-besoins-video.html',
        'only_matching': True,
    }, {
        'url': 'https://www.ina.fr/audio/P16173408',
        'only_matching': True,
    }, {
        'url': 'https://www.ina.fr/video/P16173408-video.html',
        'only_matching': True,
    }, {
        'url': 'http://m.ina.fr/video/I12055569',
        'only_matching': True,
    }, {
        'url': 'https://www.ina.fr/ina-eclaire-actu/video/cpb8205116303/les-jeux-electroniques',
        'md5': '4b8284a9a3a184fdc7e744225b8251e7',
        'info_dict': {
            'id': 'CPB8205116303',
            'ext': 'mp4',
            'title': 'Les jeux électroniques',
            'description': 'md5:e09f7683dad1cc60b74950490127d233',
            'upload_date': '19821204',
            'duration': 657,
            'thumbnail': 'https://cdn-hub.ina.fr/notice/690x517/203/CPB8205116303.jpeg',
        },
    }, {
        'url': 'https://www.ina.fr/ina-eclaire-actu/arletty-carriere-conseils-actrice-marcel-carne',
        'md5': '743d6f069a00e19dda0da166a54eeccb',
        'info_dict': {
            'id': 'I22203233',
            'ext': 'mp4',
            'title': 'Arletty sur le métier d\'actrice',
            'description': 'md5:3d89b5e419d8514c934f146045ccdbad',
            'upload_date': '19581128',
            'thumbnail': 'https://cdn-hub.ina.fr/notice/690x517/082/I22203233.jpeg',
        },
    }, {
        'url': 'https://www.ina.fr/ina-eclaire-actu/chasse-croise-sncf-gare-d-austerlitz-vacances-d-ete',
        'md5': 'a96fb85e9ba3b5c5b2eeb0c5daa55f2f',
        'info_dict': {
            'id': 'CAF91038285',
            'ext': 'mp4',
            'title': 'Les grands départs : les trains',
            'description': 'md5:1630ee819d8d4da97df53459e99f72bb',
            'upload_date': '19740801',
            'thumbnail': 'https://cdn-hub.ina.fr/notice/690x517/2cf/CAF91038285.jpeg',
        },
    }]

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

        api_url = self._html_search_regex(r'asset-details-url\s*=\s*["\'](?P<api_url>[^"\']+)', webpage, 'api_url')
        asset_id = self._search_regex(r'assets/([^?/]+)', api_url, 'asset_id')

        api_response = self._download_json(api_url.replace(asset_id, f'{asset_id}.json'), asset_id)

        return {
            'id': asset_id,
            'url': api_response['resourceUrl'],
            'ext': {'video': 'mp4', 'audio': 'mp3'}.get(api_response.get('type')),
            'title': api_response.get('title'),
            'description': api_response.get('description'),
            'upload_date': unified_strdate(api_response.get('dateOfBroadcast')),
            'duration': api_response.get('duration'),
            'thumbnail': api_response.get('resourceThumbnail'),
        }


code:
from .common import InfoExtractor
from .kaltura import KalturaIE

class IncIE(InfoExtractor):
    _VALID_URL = r'https?://(?:www\.)?inc\.com/(?:[^/]+/)+(?P<id>[^.]+).html'
    _TESTS = [{
        'url': 'http://www.inc.com/tip-sheet/bill-gates-says-these-5-books-will-make-you-smarter.html',
        'md5': '7416739c9c16438c09fa35619d6ba5cb',
        'info_dict': {
            'id': '1_wqig47aq',
            'ext': 'mov',
            'title': 'Bill Gates Says These 5 Books Will Make You Smarter',
            'description': 'md5:bea7ff6cce100886fc1995acb743237e',
            'timestamp': 1474414430,
            'upload_date': '20160920',
            'uploader_id': 'video@inc.com',
        },
        'params': {
            'skip_download': True,
        },
    }, {
        # div with id=kaltura_player_1_kqs38cgm
        'url': 'https://www.inc.com/oscar-raymundo/richard-branson-young-entrepeneurs.html',
        'info_dict': {
            'id': '1_kqs38cgm',
            'ext': 'mp4',
            'title': 'Branson: "In the end, you have to say, Screw it. Just do it."',
            'description': 'md5:21b832d034f9af5191ca5959da5e9cb6',
            'timestamp': 1364403232,
            'upload_date': '20130327',
            'uploader_id': 'incdigital@inc.com',
        },
        'params': {
            'skip_download': True,
        },
    }, {
        'url': 'http://www.inc.com/video/david-whitford/founders-forum-tripadvisor-steve-kaufer-most-enjoyable-moment-for-entrepreneur.html',
        'only_matching': True,
    }]

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

        partner_id = self._search_regex(
            r'var\s+_?bizo_data_partner_id\s*=\s*["\'](\d+)', webpage,
            'partner id', default='1034971')

        kaltura_id = self._search_regex(
            r'id=(["\'])kaltura_player_(?P<id>.+?)\1', webpage, 'kaltura id',
            default=None, group='id') or self._parse_json(self._search_regex(
                r'pageInfo\.videos\s*=\s*\[(.+)\];', webpage, 'kaltura id'),
            display_id)['vid_kaltura_id']

        return self.url_result(
            'kaltura:%s:%s' % (partner_id, kaltura_id), KalturaIE.ie_key())


code:
from .common import InfoExtractor
from ..compat import compat_str
from ..utils import (
    int_or_none,
    parse_age_limit,
    parse_iso8601,
    update_url_query,
)

class IndavideoEmbedIE(InfoExtractor):
    _VALID_URL = r'https?://(?:(?:embed\.)?indavideo\.hu/player/video/|assets\.indavideo\.hu/swf/player\.swf\?.*\b(?:v(?:ID|id))=)(?P<id>[\da-f]+)'
    # Some example URLs covered by generic extractor:
    #   http://indavideo.hu/video/Vicces_cica_1
    #   http://index.indavideo.hu/video/2015_0728_beregszasz
    #   http://auto.indavideo.hu/video/Sajat_utanfutoban_a_kis_tacsko
    #   http://erotika.indavideo.hu/video/Amator_tini_punci
    #   http://film.indavideo.hu/video/f_hrom_nagymamm_volt
    #   http://palyazat.indavideo.hu/video/Embertelen_dal_Dodgem_egyuttes
    _EMBED_REGEX = [r'<iframe[^>]+\bsrc=["\'](?P<url>(?:https?:)?//embed\.indavideo\.hu/player/video/[\da-f]+)']
    _TESTS = [{
        'url': 'http://indavideo.hu/player/video/1bdc3c6d80/',
        'md5': 'c8a507a1c7410685f83a06eaeeaafeab',
        'info_dict': {
            'id': '1837039',
            'ext': 'mp4',
            'title': 'Cicatánc',
            'description': '',
            'thumbnail': r're:^https?://.*\.jpg$',
            'uploader': 'cukiajanlo',
            'uploader_id': '83729',
            'timestamp': 1439193826,
            'upload_date': '20150810',
            'duration': 72,
            'age_limit': 0,
            'tags': ['tánc', 'cica', 'cuki', 'cukiajanlo', 'newsroom'],
        },
    }, {
        'url': 'http://embed.indavideo.hu/player/video/1bdc3c6d80?autostart=1&hide=1',
        'only_matching': True,
    }, {
        'url': 'http://assets.indavideo.hu/swf/player.swf?v=fe25e500&vID=1bdc3c6d80&autostart=1&hide=1&i=1',
        'only_matching': True,
    }]

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

        video = self._download_json(
            'https://amfphp.indavideo.hu/SYm0json.php/player.playerHandler.getVideoData/%s' % video_id,
            video_id)['data']

        title = video['title']

        video_urls = []

        video_files = video.get('video_files')
        if isinstance(video_files, list):
            video_urls.extend(video_files)
        elif isinstance(video_files, dict):
            video_urls.extend(video_files.values())

        video_file = video.get('video_file')
        if video:
            video_urls.append(video_file)
        video_urls = list(set(video_urls))

        video_prefix = video_urls[0].rsplit('/', 1)[0]

        for flv_file in video.get('flv_files', []):
            flv_url = '%s/%s' % (video_prefix, flv_file)
            if flv_url not in video_urls:
                video_urls.append(flv_url)

        filesh = video.get('filesh')

        formats = []
        for video_url in video_urls:
            height = int_or_none(self._search_regex(
                r'\.(\d{3,4})\.mp4(?:\?|$)', video_url, 'height', default=None))
            if filesh:
                if not height:
                    continue
                token = filesh.get(compat_str(height))
                if token is None:
                    continue
                video_url = update_url_query(video_url, {'token': token})
            formats.append({
                'url': video_url,
                'height': height,
            })
        self._sort_formats(formats)

        timestamp = video.get('date')
        if timestamp:
            # upload date is in CEST
            timestamp = parse_iso8601(timestamp + ' +0200', ' ')

        thumbnails = [{
            'url': self._proto_relative_url(thumbnail)
        } for thumbnail in video.get('thumbnails', [])]

        tags = [tag['title'] for tag in video.get('tags') or []]

        return {
            'id': video.get('id') or video_id,
            'title': title,
            'description': video.get('description'),
            'thumbnails': thumbnails,
            'uploader': video.get('user_name'),
            'uploader_id': video.get('user_id'),
            'timestamp': timestamp,
            'duration': int_or_none(video.get('length')),
            'age_limit': parse_age_limit(video.get('age_limit')),
            'tags': tags,
            'formats': formats,
        }


code:
from ..compat import (
    compat_b64decode,
    compat_urllib_parse_unquote,
    compat_urlparse,
)
from ..utils import (
    ExtractorError,
    determine_ext,
    update_url_query,
    traverse_obj,
)
from .bokecc import BokeCCBaseIE

class InfoQIE(BokeCCBaseIE):
    _VALID_URL = r'https?://(?:www\.)?infoq\.com/(?:[^/]+/)+(?P<id>[^/]+)'

    _TESTS = [{
        'url': 'http://www.infoq.com/presentations/A-Few-of-My-Favorite-Python-Things',
        'md5': 'b5ca0e0a8c1fed93b0e65e48e462f9a2',
        'info_dict': {
            'id': 'A-Few-of-My-Favorite-Python-Things',
            'ext': 'mp4',
            'description': 'Mike Pirnat presents some tips and tricks, standard libraries and third party packages that make programming in Python a richer experience.',
            'title': 'A Few of My Favorite [Python] Things',
        },
    }, {
        'url': 'http://www.infoq.com/fr/presentations/changez-avis-sur-javascript',
        'only_matching': True,
    }, {
        'url': 'http://www.infoq.com/cn/presentations/openstack-continued-delivery',
        'md5': '4918d0cca1497f2244572caf626687ef',
        'info_dict': {
            'id': 'openstack-continued-delivery',
            'title': 'OpenStack持续交付之路',
            'ext': 'flv',
            'description': 'md5:308d981fb28fa42f49f9568322c683ff',
        },
        'skip': 'Sorry, the page you visited does not exist',
    }, {
        'url': 'https://www.infoq.com/presentations/Simple-Made-Easy',
        'md5': '0e34642d4d9ef44bf86f66f6399672db',
        'info_dict': {
            'id': 'Simple-Made-Easy',
            'title': 'Simple Made Easy',
            'ext': 'mp3',
            'description': 'md5:3e0e213a8bbd074796ef89ea35ada25b',
        },
        'params': {
            'format': 'bestaudio',
        },
    }]

    def _extract_rtmp_video(self, webpage):
        # The server URL is hardcoded
        video_url = 'rtmpe://videof.infoq.com/cfx/st/'

        # Extract video URL
        encoded_id = self._search_regex(
            r"jsclassref\s*=\s*'([^']*)'", webpage, 'encoded id', default=None)

        real_id = compat_urllib_parse_unquote(compat_b64decode(encoded_id).decode('utf-8'))
        playpath = 'mp4:' + real_id

        return [{
            'format_id': 'rtmp_video',
            'url': video_url,
            'ext': determine_ext(playpath),
            'play_path': playpath,
        }]

    def _extract_cf_auth(self, webpage):
        policy = self._search_regex(r'InfoQConstants\.scp\s*=\s*\'([^\']+)\'', webpage, 'policy')
        signature = self._search_regex(r'InfoQConstants\.scs\s*=\s*\'([^\']+)\'', webpage, 'signature')
        key_pair_id = self._search_regex(r'InfoQConstants\.sck\s*=\s*\'([^\']+)\'', webpage, 'key-pair-id')
        return {
            'Policy': policy,
            'Signature': signature,
            'Key-Pair-Id': key_pair_id,
        }

    def _extract_http_video(self, webpage):
        http_video_url = self._search_regex(r'P\.s\s*=\s*\'([^\']+)\'', webpage, 'video URL')
        http_video_url = update_url_query(http_video_url, self._extract_cf_auth(webpage))
        return [{
            'format_id': 'http_video',
            'url': http_video_url,
            'http_headers': {'Referer': 'https://www.infoq.com/'},
        }]

    def _extract_http_audio(self, webpage, video_id):
        try:
            http_audio_url = traverse_obj(self._form_hidden_inputs('mp3Form', webpage), 'filename')
        except ExtractorError:
            http_audio_url = None
        if not http_audio_url:
            return []

        # base URL is found in the Location header in the response returned by
        # GET https://www.infoq.com/mp3download.action?filename=... when logged in.
        http_audio_url = compat_urlparse.urljoin('http://ress.infoq.com/downloads/mp3downloads/', http_audio_url)
        http_audio_url = update_url_query(http_audio_url, self._extract_cf_auth(webpage))

        # audio file seem to be missing some times even if there is a download link
        # so probe URL to make sure
        if not self._is_valid_url(http_audio_url, video_id):
            return []

        return [{
            'format_id': 'http_audio',
            'url': http_audio_url,
            'vcodec': 'none',
        }]

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

        video_title = self._html_extract_title(webpage)
        video_description = self._html_search_meta('description', webpage, 'description')

        if '/cn/' in url:
            # for China videos, HTTP video URL exists but always fails with 403
            formats = self._extract_bokecc_formats(webpage, video_id)
        else:
            formats = (
                self._extract_rtmp_video(webpage)
                + self._extract_http_video(webpage)
                + self._extract_http_audio(webpage, video_id))

        self._sort_formats(formats)

        return {
            'id': video_id,
            'title': video_title,
            'description': video_description,
            'formats': formats,
        }