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

class HTML5MediaEmbedIE(InfoExtractor):
    _VALID_URL = False
    IE_NAME = 'html5'
    _WEBPAGE_TESTS = [
        {
            'url': 'https://html.com/media/',
            'info_dict': {
                'title': 'HTML5 Media',
                'description': 'md5:933b2d02ceffe7a7a0f3c8326d91cc2a',
            },
            'playlist_count': 2
        }
    ]

    def _extract_from_webpage(self, url, webpage):
        video_id, title = self._generic_id(url), self._generic_title(url)
        entries = self._parse_html5_media_entries(url, webpage, video_id, m3u8_id='hls') or []
        for num, entry in enumerate(entries, start=1):
            entry.update({
                'id': f'{video_id}-{num}',
                'title': f'{title} ({num})',
                '_old_archive_ids': [
                    make_archive_id('generic', f'{video_id}-{num}' if len(entries) > 1 else video_id),
                ],
            })
            self._sort_formats(entry['formats'])
            yield entry

repo_name: Lesmiscore/ytdl


code:
from .common import InfoExtractor
from ..utils import (
    bool_or_none,
    ExtractorError,
    dict_get,
    float_or_none,
    int_or_none,
    str_or_none,
    traverse_obj,
    try_get,
    url_or_none,
    urljoin,
)

class GettrBaseIE(InfoExtractor):
    _BASE_REGEX = r'https?://(www\.)?gettr\.com/'
    _MEDIA_BASE_URL = 'https://media.gettr.com/'

    def _call_api(self, path, video_id, *args, **kwargs):
        return self._download_json(urljoin('https://api.gettr.com/u/', path), video_id, *args, **kwargs)['result']

class GettrIE(GettrBaseIE):
    _VALID_URL = GettrBaseIE._BASE_REGEX + r'post/(?P<id>[a-z0-9]+)'

    _TESTS = [{
        'url': 'https://www.gettr.com/post/pcf6uv838f',
        'info_dict': {
            'id': 'pcf6uv838f',
            'title': 'md5:9086a646bbd06c41c4fe8e52b3c93454',
            'description': 'md5:be0577f1e4caadc06de4a002da2bf287',
            'ext': 'mp4',
            'uploader': 'EpochTV',
            'uploader_id': 'epochtv',
            'upload_date': '20210927',
            'thumbnail': r're:^https?://.+/out\.jpg',
            'timestamp': 1632782451.058,
            'duration': 58.5585,
            'tags': ['hornofafrica', 'explorations'],
        }
    }, {
        'url': 'https://gettr.com/post/p4iahp',
        'info_dict': {
            'id': 'p4iahp',
            'title': 'md5:b03c07883db6fbc1aab88877a6c3b149',
            'description': 'md5:741b7419d991c403196ed2ea7749a39d',
            'ext': 'mp4',
            'uploader': 'Neues Forum Freiheit',
            'uploader_id': 'nf_freiheit',
            'upload_date': '20210718',
            'thumbnail': r're:^https?://.+/out\.jpg',
            'timestamp': 1626594455.017,
            'duration': 23,
            'tags': 'count:12',
        }
    }, {
        # quote post
        'url': 'https://gettr.com/post/pxn5b743a9',
        'only_matching': True,
    }, {
        # quote with video
        'url': 'https://gettr.com/post/pxtiiz5ca2',
        'only_matching': True,
    }, {
        # streaming embed
        'url': 'https://gettr.com/post/pxlu8p3b13',
        'only_matching': True,
    }, {
        # youtube embed
        'url': 'https://gettr.com/post/pv6wp9e24c',
        'only_matching': True,
        'add_ie': ['Youtube'],
    }]

    def _real_extract(self, url):
        post_id = self._match_id(url)
        webpage = self._download_webpage(url, post_id)
        api_data = self._call_api('post/%s?incl="poststats|userinfo"' % post_id, post_id)

        post_data = api_data.get('data')
        user_data = try_get(api_data, lambda x: x['aux']['uinf'][post_data['uid']], dict) or {}

        vid = post_data.get('vid')
        ovid = post_data.get('ovid')

        if post_data.get('p_type') == 'stream':
            return self.url_result(f'https://gettr.com/streaming/{post_id}', ie='GettrStreaming', video_id=post_id)

        if not (ovid or vid):
            embed_url = url_or_none(post_data.get('prevsrc'))
            shared_post_id = traverse_obj(api_data, ('aux', 'shrdpst', '_id'), ('data', 'rpstIds', 0), expected_type=str)

            if embed_url:
                return self.url_result(embed_url)
            elif shared_post_id:
                return self.url_result(f'https://gettr.com/post/{shared_post_id}', ie='Gettr', video_id=shared_post_id)
            else:
                raise ExtractorError('There\'s no video in this post.')

        title = description = str_or_none(
            post_data.get('txt') or self._og_search_description(webpage))

        uploader = str_or_none(
            user_data.get('nickname')
            or self._search_regex(r'^(.+?) on GETTR', self._og_search_title(webpage, default=''), 'uploader', fatal=False))

        if uploader:
            title = '%s - %s' % (uploader, title)

        formats, subtitles = self._extract_m3u8_formats_and_subtitles(
            urljoin(self._MEDIA_BASE_URL, vid), post_id, 'mp4',
            entry_protocol='m3u8_native', m3u8_id='hls', fatal=False) if vid else ([], {})

        if ovid:
            formats.append({
                'url': urljoin(self._MEDIA_BASE_URL, ovid),
                'format_id': 'ovid',
                'ext': 'mp4',
                'width': int_or_none(post_data.get('vid_wid')),
                'height': int_or_none(post_data.get('vid_hgt')),
            })

        self._sort_formats(formats)

        return {
            'id': post_id,
            'title': title,
            'description': description,
            'formats': formats,
            'subtitles': subtitles,
            'uploader': uploader,
            'uploader_id': str_or_none(
                dict_get(user_data, ['_id', 'username'])
                or post_data.get('uid')),
            'thumbnail': url_or_none(
                urljoin(self._MEDIA_BASE_URL, post_data.get('main'))
                or self._html_search_meta(['og:image', 'image'], webpage, 'thumbnail', fatal=False)),
            'timestamp': float_or_none(dict_get(post_data, ['cdate', 'udate']), scale=1000),
            'duration': float_or_none(post_data.get('vid_dur')),
            'tags': post_data.get('htgs'),
        }

class GettrStreamingIE(GettrBaseIE):
    _VALID_URL = GettrBaseIE._BASE_REGEX + r'streaming/(?P<id>[a-z0-9]+)'

    _TESTS = [{
        'url': 'https://gettr.com/streaming/psoiulc122',
        'info_dict': {
            'id': 'psoiulc122',
            'ext': 'mp4',
            'description': 'md5:56bca4b8f48f1743d9fd03d49c723017',
            'view_count': int,
            'uploader': 'Corona Investigative Committee',
            'uploader_id': 'coronacommittee',
            'duration': 5180.184,
            'thumbnail': r're:^https?://.+',
            'title': 'Day 1: Opening Session of the Grand Jury Proceeding',
            'timestamp': 1644080997.164,
            'upload_date': '20220205',
        }
    }, {
        'url': 'https://gettr.com/streaming/psfmeefcc1',
        'info_dict': {
            'id': 'psfmeefcc1',
            'ext': 'mp4',
            'title': 'Session 90: "The Virus Of Power"',
            'view_count': int,
            'uploader_id': 'coronacommittee',
            'description': 'md5:98986acdf656aa836bf36f9c9704c65b',
            'uploader': 'Corona Investigative Committee',
            'thumbnail': r're:^https?://.+',
            'duration': 21872.507,
            'timestamp': 1643976662.858,
            'upload_date': '20220204',
        }
    }]

    def _real_extract(self, url):
        video_id = self._match_id(url)
        video_info = self._call_api('live/join/%s' % video_id, video_id, data={})

        live_info = video_info['broadcast']
        live_url = url_or_none(live_info.get('url'))

        formats, subtitles = self._extract_m3u8_formats_and_subtitles(
            live_url, video_id, ext='mp4',
            entry_protocol='m3u8_native', m3u8_id='hls', fatal=False) if live_url else ([], {})

        thumbnails = [{
            'url': urljoin(self._MEDIA_BASE_URL, thumbnail),
        } for thumbnail in try_get(video_info, lambda x: x['postData']['imgs'], list) or []]

        self._sort_formats(formats)

        return {
            'id': video_id,
            'title': try_get(video_info, lambda x: x['postData']['ttl'], str),
            'description': try_get(video_info, lambda x: x['postData']['dsc'], str),
            'formats': formats,
            'subtitles': subtitles,
            'thumbnails': thumbnails,
            'uploader': try_get(video_info, lambda x: x['liveHostInfo']['nickname'], str),
            'uploader_id': try_get(video_info, lambda x: x['liveHostInfo']['_id'], str),
            'view_count': int_or_none(live_info.get('viewsCount')),
            'timestamp': float_or_none(live_info.get('startAt'), scale=1000),
            'duration': float_or_none(live_info.get('duration'), scale=1000),
            'is_live': bool_or_none(live_info.get('isLive')),
        }


code:
from .common import InfoExtractor
from ..utils import (
    int_or_none,
    float_or_none,
    qualities,
    ExtractorError,
)

class GfycatIE(InfoExtractor):
    _VALID_URL = r'https?://(?:(?:www|giant|thumbs)\.)?gfycat\.com/(?i:ru/|ifr/|gifs/detail/)?(?P<id>[^-/?#\."\']+)'
    _EMBED_REGEX = [rf'<(?:iframe|source)[^>]+\bsrc=["\'](?P<url>{_VALID_URL})']
    _TESTS = [{
        'url': 'http://gfycat.com/DeadlyDecisiveGermanpinscher',
        'info_dict': {
            'id': 'DeadlyDecisiveGermanpinscher',
            'ext': 'mp4',
            'title': 'Ghost in the Shell',
            'timestamp': 1410656006,
            'upload_date': '20140914',
            'uploader': 'anonymous',
            'duration': 10.4,
            'view_count': int,
            'like_count': int,
            'categories': list,
            'age_limit': 0,
            'uploader_id': 'anonymous',
            'description': '',
        }
    }, {
        'url': 'http://gfycat.com/ifr/JauntyTimelyAmazontreeboa',
        'info_dict': {
            'id': 'JauntyTimelyAmazontreeboa',
            'ext': 'mp4',
            'title': 'JauntyTimelyAmazontreeboa',
            'timestamp': 1411720126,
            'upload_date': '20140926',
            'uploader': 'anonymous',
            'duration': 3.52,
            'view_count': int,
            'like_count': int,
            'categories': list,
            'age_limit': 0,
            'uploader_id': 'anonymous',
            'description': '',
        }
    }, {
        'url': 'https://gfycat.com/alienatedsolidgreathornedowl',
        'info_dict': {
            'id': 'alienatedsolidgreathornedowl',
            'ext': 'mp4',
            'upload_date': '20211226',
            'uploader_id': 'reactions',
            'timestamp': 1640536930,
            'like_count': int,
            'description': '',
            'title': 'Ingrid Michaelson, Zooey Deschanel - Merry Christmas Happy New Year',
            'categories': list,
            'age_limit': 0,
            'duration': 2.9583333333333335,
            'uploader': 'Reaction GIFs',
            'view_count': int,
        }
    }, {
        'url': 'https://gfycat.com/ru/RemarkableDrearyAmurstarfish',
        'only_matching': True
    }, {
        'url': 'https://gfycat.com/gifs/detail/UnconsciousLankyIvorygull',
        'only_matching': True
    }, {
        'url': 'https://gfycat.com/acceptablehappygoluckyharborporpoise-baseball',
        'only_matching': True
    }, {
        'url': 'https://thumbs.gfycat.com/acceptablehappygoluckyharborporpoise-size_restricted.gif',
        'only_matching': True
    }, {
        'url': 'https://giant.gfycat.com/acceptablehappygoluckyharborporpoise.mp4',
        'only_matching': True
    }, {
        'url': 'http://gfycat.com/IFR/JauntyTimelyAmazontreeboa',
        'only_matching': True
    }]

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

        gfy = self._download_json(
            'https://api.gfycat.com/v1/gfycats/%s' % video_id,
            video_id, 'Downloading video info')
        if 'error' in gfy:
            raise ExtractorError('Gfycat said: ' + gfy['error'], expected=True)
        gfy = gfy['gfyItem']

        title = gfy.get('title') or gfy['gfyName']
        description = gfy.get('description')
        timestamp = int_or_none(gfy.get('createDate'))
        uploader = gfy.get('userName') or gfy.get('username')
        view_count = int_or_none(gfy.get('views'))
        like_count = int_or_none(gfy.get('likes'))
        dislike_count = int_or_none(gfy.get('dislikes'))
        age_limit = 18 if gfy.get('nsfw') == '1' else 0

        width = int_or_none(gfy.get('width'))
        height = int_or_none(gfy.get('height'))
        fps = int_or_none(gfy.get('frameRate'))
        num_frames = int_or_none(gfy.get('numFrames'))

        duration = float_or_none(num_frames, fps) if num_frames and fps else None

        categories = gfy.get('tags') or gfy.get('extraLemmas') or []

        FORMATS = ('gif', 'webm', 'mp4')
        quality = qualities(FORMATS)

        formats = []
        for format_id in FORMATS:
            video_url = gfy.get('%sUrl' % format_id)
            if not video_url:
                continue
            filesize = int_or_none(gfy.get('%sSize' % format_id))
            formats.append({
                'url': video_url,
                'format_id': format_id,
                'width': width,
                'height': height,
                'fps': fps,
                'filesize': filesize,
                'quality': quality(format_id),
            })
        self._sort_formats(formats)

        return {
            'id': video_id,
            'title': title,
            'description': description,
            'timestamp': timestamp,
            'uploader': gfy.get('userDisplayName') or uploader,
            'uploader_id': uploader,
            'duration': duration,
            'view_count': view_count,
            'like_count': like_count,
            'dislike_count': dislike_count,
            'categories': categories,
            'age_limit': age_limit,
            'formats': formats,
        }


code:
import json

from .common import InfoExtractor
from ..utils import (
    determine_ext,
    int_or_none,
    qualities,
    unescapeHTML,
)

class GiantBombIE(InfoExtractor):
    _VALID_URL = r'https?://(?:www\.)?giantbomb\.com/(?:videos|shows)/(?P<display_id>[^/]+)/(?P<id>\d+-\d+)'
    _TESTS = [{
        'url': 'http://www.giantbomb.com/videos/quick-look-destiny-the-dark-below/2300-9782/',
        'md5': '132f5a803e7e0ab0e274d84bda1e77ae',
        'info_dict': {
            'id': '2300-9782',
            'display_id': 'quick-look-destiny-the-dark-below',
            'ext': 'mp4',
            'title': 'Quick Look: Destiny: The Dark Below',
            'description': 'md5:0aa3aaf2772a41b91d44c63f30dfad24',
            'duration': 2399,
            'thumbnail': r're:^https?://.*\.jpg$',
        }
    }, {
        'url': 'https://www.giantbomb.com/shows/ben-stranding/2970-20212',
        'only_matching': True,
    }]

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

        webpage = self._download_webpage(url, display_id)

        title = self._og_search_title(webpage)
        description = self._og_search_description(webpage)
        thumbnail = self._og_search_thumbnail(webpage)

        video = json.loads(unescapeHTML(self._search_regex(
            r'data-video="([^"]+)"', webpage, 'data-video')))

        duration = int_or_none(video.get('lengthSeconds'))

        quality = qualities([
            'f4m_low', 'progressive_low', 'f4m_high',
            'progressive_high', 'f4m_hd', 'progressive_hd'])

        formats = []
        for format_id, video_url in video['videoStreams'].items():
            if format_id == 'f4m_stream':
                continue
            ext = determine_ext(video_url)
            if ext == 'f4m':
                f4m_formats = self._extract_f4m_formats(video_url + '?hdcore=3.3.1', display_id)
                if f4m_formats:
                    f4m_formats[0]['quality'] = quality(format_id)
                    formats.extend(f4m_formats)
            elif ext == 'm3u8':
                formats.extend(self._extract_m3u8_formats(
                    video_url, display_id, ext='mp4', entry_protocol='m3u8_native',
                    m3u8_id='hls', fatal=False))
            else:
                formats.append({
                    'url': video_url,
                    'format_id': format_id,
                    'quality': quality(format_id),
                })

        if not formats:
            youtube_id = video.get('youtubeID')
            if youtube_id:
                return self.url_result(youtube_id, 'Youtube')

        self._sort_formats(formats)

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


code:
import itertools

from .common import InfoExtractor
from ..compat import compat_str
from ..utils import parse_duration, parse_iso8601, qualities, str_to_int

class GigaIE(InfoExtractor):
    _VALID_URL = r'https?://(?:www\.)?giga\.de/(?:[^/]+/)*(?P<id>[^/]+)'
    _TESTS = [{
        'url': 'http://www.giga.de/filme/anime-awesome/trailer/anime-awesome-chihiros-reise-ins-zauberland-das-beste-kommt-zum-schluss/',
        'md5': '6bc5535e945e724640664632055a584f',
        'info_dict': {
            'id': '2622086',
            'display_id': 'anime-awesome-chihiros-reise-ins-zauberland-das-beste-kommt-zum-schluss',
            'ext': 'mp4',
            'title': 'Anime Awesome: Chihiros Reise ins Zauberland – Das Beste kommt zum Schluss',
            'description': 'md5:afdf5862241aded4718a30dff6a57baf',
            'thumbnail': r're:^https?://.*\.jpg$',
            'duration': 578,
            'timestamp': 1414749706,
            'upload_date': '20141031',
            'uploader': 'Robin Schweiger',
            'view_count': int,
        },
    }, {
        'url': 'http://www.giga.de/games/channel/giga-top-montag/giga-topmontag-die-besten-serien-2014/',
        'only_matching': True,
    }, {
        'url': 'http://www.giga.de/extra/netzkultur/videos/giga-games-tom-mats-robin-werden-eigene-wege-gehen-eine-ankuendigung/',
        'only_matching': True,
    }, {
        'url': 'http://www.giga.de/tv/jonas-liest-spieletitel-eingedeutscht-episode-2/',
        'only_matching': True,
    }]

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

        webpage = self._download_webpage(url, display_id)

        video_id = self._search_regex(
            [r'data-video-id="(\d+)"', r'/api/video/jwplayer/#v=(\d+)'],
            webpage, 'video id')

        playlist = self._download_json(
            'http://www.giga.de/api/syndication/video/video_id/%s/playlist.json?content=syndication/key/368b5f151da4ae05ced7fa296bdff65a/'
            % video_id, video_id)[0]

        quality = qualities(['normal', 'hd720'])

        formats = []
        for format_id in itertools.count(0):
            fmt = playlist.get(compat_str(format_id))
            if not fmt:
                break
            formats.append({
                'url': fmt['src'],
                'format_id': '%s-%s' % (fmt['quality'], fmt['type'].split('/')[-1]),
                'quality': quality(fmt['quality']),
            })
        self._sort_formats(formats)

        title = self._html_search_meta(
            'title', webpage, 'title', fatal=True)
        description = self._html_search_meta(
            'description', webpage, 'description')
        thumbnail = self._og_search_thumbnail(webpage)

        duration = parse_duration(self._search_regex(
            r'(?s)(?:data-video-id="{0}"|data-video="[^"]*/api/video/jwplayer/#v={0}[^"]*")[^>]*>.+?([^<]+)'.format(video_id),
            webpage, 'duration', fatal=False))

        timestamp = parse_iso8601(self._search_regex(
            r'datetime="([^"]+)"', webpage, 'upload date', fatal=False))
        uploader = self._search_regex(
            r'class="author">([^<]+)', webpage, 'uploader', fatal=False)

        view_count = str_to_int(self._search_regex(
            r'([\d.,]+)',
            webpage, 'view count', fatal=False))

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


code:
from .common import InfoExtractor

from ..utils import (
    ExtractorError,
    urlencode_postdata,
)

class GigyaBaseIE(InfoExtractor):
    def _gigya_login(self, auth_data):
        auth_info = self._download_json(
            'https://accounts.eu1.gigya.com/accounts.login', None,
            note='Logging in', errnote='Unable to log in',
            data=urlencode_postdata(auth_data))

        error_message = auth_info.get('errorDetails') or auth_info.get('errorMessage')
        if error_message:
            raise ExtractorError(
                'Unable to login: %s' % error_message, expected=True)
        return auth_info


code:
from .common import InfoExtractor

class GlideIE(InfoExtractor):
    IE_DESC = 'Glide mobile video messages (glide.me)'
    _VALID_URL = r'https?://share\.glide\.me/(?P<id>[A-Za-z0-9\-=_+]+)'
    _TEST = {
        'url': 'http://share.glide.me/UZF8zlmuQbe4mr+7dCiQ0w==',
        'md5': '4466372687352851af2d131cfaa8a4c7',
        'info_dict': {
            'id': 'UZF8zlmuQbe4mr+7dCiQ0w==',
            'ext': 'mp4',
            'title': "Damon's Glide message",
            'thumbnail': r're:^https?://.*?\.cloudfront\.net/.*\.jpg$',
        }
    }

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

        webpage = self._download_webpage(url, video_id)

        title = self._html_extract_title(webpage, default=None) or self._og_search_title(webpage)
        video_url = self._proto_relative_url(self._search_regex(
            r'<source[^>]+src=(["\'])(?P<url>.+?)\1',
            webpage, 'video URL', default=None,
            group='url')) or self._og_search_video_url(webpage)
        thumbnail = self._proto_relative_url(self._search_regex(
            r'<img[^>]+id=["\']video-thumbnail["\'][^>]+src=(["\'])(?P<url>.+?)\1',
            webpage, 'thumbnail url', default=None,
            group='url')) or self._og_search_thumbnail(webpage)

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


code:
import base64
import hashlib
import json
import random
import re

from .common import InfoExtractor
from ..compat import (
    compat_str,
)
from ..utils import (
    HEADRequest,
    ExtractorError,
    float_or_none,
    orderedSet,
    str_or_none,
    try_get,
)

class GloboIE(InfoExtractor):
    _VALID_URL = r'(?:globo:|https?://.+?\.globo\.com/(?:[^/]+/)*(?:v/(?:[^/]+/)?|videos/))(?P<id>\d{7,})'
    _NETRC_MACHINE = 'globo'
    _TESTS = [{
        'url': 'http://g1.globo.com/carros/autoesporte/videos/t/exclusivos-do-g1/v/mercedes-benz-gla-passa-por-teste-de-colisao-na-europa/3607726/',
        'info_dict': {
            'id': '3607726',
            'ext': 'mp4',
            'title': 'Mercedes-Benz GLA passa por teste de colisão na Europa',
            'duration': 103.204,
            'uploader': 'G1',
            'uploader_id': '2015',
        },
        'params': {
            'skip_download': True,
        },
    }, {
        'url': 'http://globoplay.globo.com/v/4581987/',
        'info_dict': {
            'id': '4581987',
            'ext': 'mp4',
            'title': 'Acidentes de trânsito estão entre as maiores causas de queda de energia em SP',
            'duration': 137.973,
            'uploader': 'Rede Globo',
            'uploader_id': '196',
        },
        'params': {
            'skip_download': True,
        },
    }, {
        'url': 'http://canalbrasil.globo.com/programas/sangue-latino/videos/3928201.html',
        'only_matching': True,
    }, {
        'url': 'http://globosatplay.globo.com/globonews/v/4472924/',
        'only_matching': True,
    }, {
        'url': 'http://globotv.globo.com/t/programa/v/clipe-sexo-e-as-negas-adeus/3836166/',
        'only_matching': True,
    }, {
        'url': 'http://globotv.globo.com/canal-brasil/sangue-latino/t/todos-os-videos/v/ator-e-diretor-argentino-ricado-darin-fala-sobre-utopias-e-suas-perdas/3928201/',
        'only_matching': True,
    }, {
        'url': 'http://canaloff.globo.com/programas/desejar-profundo/videos/4518560.html',
        'only_matching': True,
    }, {
        'url': 'globo:3607726',
        'only_matching': True,
    }, {
        'url': 'https://globoplay.globo.com/v/10248083/',
        'info_dict': {
            'id': '10248083',
            'ext': 'mp4',
            'title': 'Melhores momentos: Equador 1 x 1 Brasil pelas Eliminatórias da Copa do Mundo 2022',
            'duration': 530.964,
            'uploader': 'SporTV',
            'uploader_id': '698',
        },
        'params': {
            'skip_download': True,
        },
    }]

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

        self._request_webpage(
            HEADRequest('https://globo-ab.globo.com/v2/selected-alternatives?experiments=player-isolated-experiment-02&skipImpressions=true'),
            video_id, 'Getting cookies')

        video = self._download_json(
            'http://api.globovideos.com/videos/%s/playlist' % video_id,
            video_id)['videos'][0]
        if not self.get_param('allow_unplayable_formats') and video.get('encrypted') is True:
            self.report_drm(video_id)

        title = video['title']

        formats = []
        security = self._download_json(
            'https://playback.video.globo.com/v2/video-session', video_id, 'Downloading security hash for %s' % video_id,
            headers={'content-type': 'application/json'}, data=json.dumps({
                "player_type": "desktop",
                "video_id": video_id,
                "quality": "max",
                "content_protection": "widevine",
                "vsid": "581b986b-4c40-71f0-5a58-803e579d5fa2",
                "tz": "-3.0:00"
            }).encode())

        self._request_webpage(HEADRequest(security['sources'][0]['url_template']), video_id, 'Getting locksession cookie')

        security_hash = security['sources'][0]['token']
        if not security_hash:
            message = security.get('message')
            if message:
                raise ExtractorError(
                    '%s returned error: %s' % (self.IE_NAME, message), expected=True)

        hash_code = security_hash[:2]
        padding = '%010d' % random.randint(1, 10000000000)
        if hash_code in ('04', '14'):
            received_time = security_hash[3:13]
            received_md5 = security_hash[24:]
            hash_prefix = security_hash[:23]
        elif hash_code in ('02', '12', '03', '13'):
            received_time = security_hash[2:12]
            received_md5 = security_hash[22:]
            padding += '1'
            hash_prefix = '05' + security_hash[:22]

        padded_sign_time = compat_str(int(received_time) + 86400) + padding
        md5_data = (received_md5 + padded_sign_time + '0xAC10FD').encode()
        signed_md5 = base64.urlsafe_b64encode(hashlib.md5(md5_data).digest()).decode().strip('=')
        signed_hash = hash_prefix + padded_sign_time + signed_md5
        source = security['sources'][0]['url_parts']
        resource_url = source['scheme'] + '://' + source['domain'] + source['path']
        signed_url = '%s?h=%s&k=html5&a=%s' % (resource_url, signed_hash, 'F' if video.get('subscriber_only') else 'A')

        fmts, subtitles = self._extract_m3u8_formats_and_subtitles(
            signed_url, video_id, 'mp4', entry_protocol='m3u8_native', m3u8_id='hls', fatal=False)
        formats.extend(fmts)
        self._sort_formats(formats)

        for resource in video['resources']:
            if resource.get('type') == 'subtitle':
                subtitles.setdefault(resource.get('language') or 'por', []).append({
                    'url': resource.get('url'),
                })
        subs = try_get(security, lambda x: x['source']['subtitles'], expected_type=dict) or {}
        for sub_lang, sub_url in subs.items():
            if sub_url:
                subtitles.setdefault(sub_lang or 'por', []).append({
                    'url': sub_url,
                })
        subs = try_get(security, lambda x: x['source']['subtitles_webvtt'], expected_type=dict) or {}
        for sub_lang, sub_url in subs.items():
            if sub_url:
                subtitles.setdefault(sub_lang or 'por', []).append({
                    'url': sub_url,
                })

        duration = float_or_none(video.get('duration'), 1000)
        uploader = video.get('channel')
        uploader_id = str_or_none(video.get('channel_id'))

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

class GloboArticleIE(InfoExtractor):
    _VALID_URL = r'https?://.+?\.globo\.com/(?:[^/]+/)*(?P<id>[^/.]+)(?:\.html)?'

    _VIDEOID_REGEXES = [
        r'\bdata-video-id=["\'](\d{7,})["\']',
        r'\bdata-player-videosids=["\'](\d{7,})["\']',
        r'\bvideosIDs\s*:\s*["\']?(\d{7,})',
        r'\bdata-id=["\'](\d{7,})["\']',
        r'<div[^>]+\bid=["\'](\d{7,})["\']',
        r'<bs-player[^>]+\bvideoid=["\'](\d{8,})["\']',
    ]

    _TESTS = [{
        'url': 'http://g1.globo.com/jornal-nacional/noticia/2014/09/novidade-na-fiscalizacao-de-bagagem-pela-receita-provoca-discussoes.html',
        'info_dict': {
            'id': 'novidade-na-fiscalizacao-de-bagagem-pela-receita-provoca-discussoes',
            'title': 'Novidade na fiscalização de bagagem pela Receita provoca discussões',
            'description': 'md5:c3c4b4d4c30c32fce460040b1ac46b12',
        },
        'playlist_count': 1,
    }, {
        'url': 'http://g1.globo.com/pr/parana/noticia/2016/09/mpf-denuncia-lula-marisa-e-mais-seis-na-operacao-lava-jato.html',
        'info_dict': {
            'id': 'mpf-denuncia-lula-marisa-e-mais-seis-na-operacao-lava-jato',
            'title': "Lula era o 'comandante máximo' do esquema da Lava Jato, diz MPF",
            'description': 'md5:8aa7cc8beda4dc71cc8553e00b77c54c',
        },
        'playlist_count': 6,
    }, {
        'url': 'http://gq.globo.com/Prazeres/Poder/noticia/2015/10/all-o-desafio-assista-ao-segundo-capitulo-da-serie.html',
        'only_matching': True,
    }, {
        'url': 'http://gshow.globo.com/programas/tv-xuxa/O-Programa/noticia/2014/01/xuxa-e-junno-namoram-muuuito-em-luau-de-zeze-di-camargo-e-luciano.html',
        'only_matching': True,
    }, {
        'url': 'http://oglobo.globo.com/rio/a-amizade-entre-um-entregador-de-farmacia-um-piano-19946271',
        'only_matching': True,
    }, {
        'url': 'https://ge.globo.com/video/ta-na-area-como-foi-assistir-ao-jogo-do-palmeiras-que-a-globo-nao-passou-10287094.ghtml',
        'info_dict': {
            'id': 'ta-na-area-como-foi-assistir-ao-jogo-do-palmeiras-que-a-globo-nao-passou-10287094',
            'title': 'Tá na Área: como foi assistir ao jogo do Palmeiras que a Globo não passou',
            'description': 'md5:2d089d036c4c9675117d3a56f8c61739',
        },
        'playlist_count': 1,
    }, {
        'url': 'https://redeglobo.globo.com/rpc/meuparana/noticia/a-producao-de-chocolates-no-parana.ghtml',
        'info_dict': {
            'id': 'a-producao-de-chocolates-no-parana',
            'title': 'A produção de chocolates no Paraná',
            'description': 'md5:f2e3daf00ffd1dc0e9a8a6c7cfb0a89e',
        },
        'playlist_count': 2,
    }]

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

    def _real_extract(self, url):
        display_id = self._match_id(url)
        webpage = self._download_webpage(url, display_id)
        video_ids = []
        for video_regex in self._VIDEOID_REGEXES:
            video_ids.extend(re.findall(video_regex, webpage))
        entries = [
            self.url_result('globo:%s' % video_id, GloboIE.ie_key())
            for video_id in orderedSet(video_ids)]
        title = self._og_search_title(webpage).strip()
        description = self._html_search_meta('description', webpage)
        return self.playlist_result(entries, display_id, title, description)


code:
import re
import urllib.parse

from .common import InfoExtractor
from ..utils import (
    determine_ext,
    extract_attributes,
    ExtractorError,
    int_or_none,
    parse_qs,
    smuggle_url,
    unescapeHTML,
    unsmuggle_url,
)

class GlomexBaseIE(InfoExtractor):
    _DEFAULT_ORIGIN_URL = 'https://player.glomex.com/'
    _API_URL = 'https://integration-cloudfront-eu-west-1.mes.glomex.cloud/'

    @staticmethod
    def _smuggle_origin_url(url, origin_url):
        if origin_url is None:
            return url
        return smuggle_url(url, {'origin': origin_url})

    @classmethod
    def _unsmuggle_origin_url(cls, url, fallback_origin_url=None):
        defaults = {'origin': fallback_origin_url or cls._DEFAULT_ORIGIN_URL}
        unsmuggled_url, data = unsmuggle_url(url, default=defaults)
        return unsmuggled_url, data['origin']

    def _get_videoid_type(self, video_id):
        _VIDEOID_TYPES = {
            'v': 'video',
            'pl': 'playlist',
            'rl': 'related videos playlist',
            'cl': 'curated playlist',
        }
        prefix = video_id.split('-')[0]
        return _VIDEOID_TYPES.get(prefix, 'unknown type')

    def _download_api_data(self, video_id, integration, current_url=None):
        query = {
            'integration_id': integration,
            'playlist_id': video_id,
            'current_url': current_url or self._DEFAULT_ORIGIN_URL,
        }
        video_id_type = self._get_videoid_type(video_id)
        return self._download_json(
            self._API_URL,
            video_id, 'Downloading %s JSON' % video_id_type,
            'Unable to download %s JSON' % video_id_type,
            query=query)

    def _download_and_extract_api_data(self, video_id, integration, current_url):
        api_data = self._download_api_data(video_id, integration, current_url)
        videos = api_data['videos']
        if not videos:
            raise ExtractorError('no videos found for %s' % video_id)
        videos = [self._extract_api_data(video, video_id) for video in videos]
        return videos[0] if len(videos) == 1 else self.playlist_result(videos, video_id)

    def _extract_api_data(self, video, video_id):
        if video.get('error_code') == 'contentGeoblocked':
            self.raise_geo_restricted(countries=video['geo_locations'])

        formats, subs = [], {}
        for format_id, format_url in video['source'].items():
            ext = determine_ext(format_url)
            if ext == 'm3u8':
                formats_, subs_ = self._extract_m3u8_formats_and_subtitles(
                    format_url, video_id, 'mp4', m3u8_id=format_id,
                    fatal=False)
                formats.extend(formats_)
                self._merge_subtitles(subs_, target=subs)
            else:
                formats.append({
                    'url': format_url,
                    'format_id': format_id,
                })
        if video.get('language'):
            for fmt in formats:
                fmt['language'] = video['language']
        self._sort_formats(formats)

        images = (video.get('images') or []) + [video.get('image') or {}]
        thumbnails = [{
            'id': image.get('id'),
            'url': f'{image["url"]}/profile:player-960x540',
            'width': 960,
            'height': 540,
        } for image in images if image.get('url')]
        self._remove_duplicate_formats(thumbnails)

        return {
            'id': video.get('clip_id') or video_id,
            'title': video.get('title'),
            'description': video.get('description'),
            'thumbnails': thumbnails,
            'duration': int_or_none(video.get('clip_duration')),
            'timestamp': video.get('created_at'),
            'formats': formats,
            'subtitles': subs,
        }

class GlomexIE(GlomexBaseIE):
    IE_NAME = 'glomex'
    IE_DESC = 'Glomex videos'
    _VALID_URL = r'https?://video\.glomex\.com/[^/]+/(?P<id>v-[^-]+)'
    _INTEGRATION_ID = '19syy24xjn1oqlpc'

    _TESTS = [{
        'url': 'https://video.glomex.com/sport/v-cb24uwg77hgh-nach-2-0-sieg-guardiola-mit-mancity-vor-naechstem-titel',
        'md5': 'cec33a943c4240c9cb33abea8c26242e',
        'info_dict': {
            'id': 'v-cb24uwg77hgh',
            'ext': 'mp4',
            'title': 'md5:38a90cedcfadd72982c81acf13556e0c',
            'description': 'md5:1ea6b6caff1443fcbbba159e432eedb8',
            'duration': 29600,
            'timestamp': 1619895017,
            'upload_date': '20210501',
        },
    }]

    def _real_extract(self, url):
        video_id = self._match_id(url)
        return self.url_result(
            GlomexEmbedIE.build_player_url(video_id, self._INTEGRATION_ID, url),
            GlomexEmbedIE.ie_key(), video_id)

class GlomexEmbedIE(GlomexBaseIE):
    IE_NAME = 'glomex:embed'
    IE_DESC = 'Glomex embedded videos'
    _BASE_PLAYER_URL = '//player.glomex.com/integration/1/iframe-player.html'
    _BASE_PLAYER_URL_RE = re.escape(_BASE_PLAYER_URL).replace('/1/', r'/[^/]/')
    _VALID_URL = rf'https?:{_BASE_PLAYER_URL_RE}\?([^#]+&)?playlistId=(?P<id>[^#&]+)'

    _TESTS = [{
        'url': 'https://player.glomex.com/integration/1/iframe-player.html?integrationId=4059a013k56vb2yd&playlistId=v-cfa6lye0dkdd-sf',
        'md5': '68f259b98cc01918ac34180142fce287',
        'info_dict': {
            'id': 'v-cfa6lye0dkdd-sf',
            'ext': 'mp4',
            'timestamp': 1635337199,
            'duration': 133080,
            'upload_date': '20211027',
            'description': 'md5:e741185fc309310ff5d0c789b437be66',
            'title': 'md5:35647293513a6c92363817a0fb0a7961',
        },
    }, {
        'url': 'https://player.glomex.com/integration/1/iframe-player.html?origin=fullpage&integrationId=19syy24xjn1oqlpc&playlistId=rl-vcb49w1fb592p&playlistIndex=0',
        'info_dict': {
            'id': 'rl-vcb49w1fb592p',
        },
        'playlist_count': 100,
    }, {
        'url': 'https://player.glomex.com/integration/1/iframe-player.html?playlistId=cl-bgqaata6aw8x&integrationId=19syy24xjn1oqlpc',
        'info_dict': {
            'id': 'cl-bgqaata6aw8x',
        },
        'playlist_mincount': 2,
    }]

    @classmethod
    def build_player_url(cls, video_id, integration, origin_url=None):
        query_string = urllib.parse.urlencode({
            'playlistId': video_id,
            'integrationId': integration,
        })
        return cls._smuggle_origin_url(f'https:{cls._BASE_PLAYER_URL}?{query_string}', origin_url)

    @classmethod
    def _extract_embed_urls(cls, url, webpage):
        # https://docs.glomex.com/publisher/video-player-integration/javascript-api/
        quot_re = r'["\']'

        regex = fr'''(?x)
            <iframe[^>]+?src=(?P<q>{quot_re})(?P<url>
                (?:https?:)?{cls._BASE_PLAYER_URL_RE}\?(?:(?!(?P=q)).)+
            )(?P=q)'''
        for mobj in re.finditer(regex, webpage):
            embed_url = unescapeHTML(mobj.group('url'))
            if cls.suitable(embed_url):
                yield cls._smuggle_origin_url(embed_url, url)

        regex = fr'''(?x)
            <glomex-player [^>]+?>|
            <div[^>]* data-glomex-player=(?P<q>{quot_re})true(?P=q)[^>]*>'''
        for mobj in re.finditer(regex, webpage):
            attrs = extract_attributes(mobj.group(0))
            if attrs.get('data-integration-id') and attrs.get('data-playlist-id'):
                yield cls.build_player_url(attrs['data-playlist-id'], attrs['data-integration-id'], url)

        # naive parsing of inline scripts for hard-coded integration parameters
        regex = fr'''(?x)
            (?P<is_js>dataset\.)?%s\s*(?(is_js)=|:)\s*
            (?P<q>{quot_re})(?P<id>(?:(?!(?P=q)).)+)(?P=q)\s'''
        for mobj in re.finditer(r'(?x)<script[^<]*>.+?</script>', webpage):
            script = mobj.group(0)
            integration_id = re.search(regex % 'integrationId', script)
            if not integration_id:
                continue
            playlist_id = re.search(regex % 'playlistId', script)
            if playlist_id:
                yield cls.build_player_url(playlist_id, integration_id, url)

    def _real_extract(self, url):
        url, origin_url = self._unsmuggle_origin_url(url)
        playlist_id = self._match_id(url)
        integration = parse_qs(url).get('integrationId', [None])[0]
        if not integration:
            raise ExtractorError('No integrationId in URL', expected=True)
        return self._download_and_extract_api_data(playlist_id, integration, origin_url)


code:
import re

from .adobepass import AdobePassIE
from ..compat import compat_str
from ..utils import (
    int_or_none,
    determine_ext,
    parse_age_limit,
    remove_start,
    remove_end,
    try_get,
    urlencode_postdata,
    ExtractorError,
    unified_timestamp,
    traverse_obj,
)

class GoIE(AdobePassIE):
    _SITE_INFO = {
        'abc': {
            'brand': '001',
            'requestor_id': 'ABC',
        },
        'freeform': {
            'brand': '002',
            'requestor_id': 'ABCFamily',
        },
        'watchdisneychannel': {
            'brand': '004',
            'resource_id': 'Disney',
        },
        'watchdisneyjunior': {
            'brand': '008',
            'resource_id': 'DisneyJunior',
        },
        'watchdisneyxd': {
            'brand': '009',
            'resource_id': 'DisneyXD',
        },
        'disneynow': {
            'brand': '011',
            'resource_id': 'Disney',
        },
        'fxnow.fxnetworks': {
            'brand': '025',
            'requestor_id': 'dtci',
        },
    }
    _VALID_URL = r'''(?x)
                    https?://
                        (?P<sub_domain>
                            (?:%s\.)?go|fxnow\.fxnetworks|
                            (?:www\.)?(?:abc|freeform|disneynow)
                        )\.com/
                        (?:
                            (?:[^/]+/)*(?P<id>[Vv][Dd][Kk][Aa]\w+)|
                            (?:[^/]+/)*(?P<display_id>[^/?\#]+)
                        )
                    ''' % r'\.|'.join(list(_SITE_INFO.keys()))
    _TESTS = [{
        'url': 'http://abc.go.com/shows/designated-survivor/video/most-recent/VDKA3807643',
        'info_dict': {
            'id': 'VDKA3807643',
            'ext': 'mp4',
            'title': 'The Traitor in the White House',
            'description': 'md5:05b009d2d145a1e85d25111bd37222e8',
        },
        'params': {
            # m3u8 download
            'skip_download': True,
        },
        'skip': 'This content is no longer available.',
    }, {
        'url': 'https://disneynow.com/shows/big-hero-6-the-series',
        'info_dict': {
            'title': 'Doraemon',
            'id': 'SH55574025',
        },
        'playlist_mincount': 51,
    }, {
        'url': 'http://freeform.go.com/shows/shadowhunters/episodes/season-2/1-this-guilty-blood',
        'info_dict': {
            'id': 'VDKA3609139',
            'title': 'This Guilty Blood',
            'description': 'md5:f18e79ad1c613798d95fdabfe96cd292',
            'age_limit': 14,
            'episode': 'Episode 1',
            'upload_date': '20170102',
            'season': 'Season 2',
            'thumbnail': 'http://cdn1.edgedatg.com/aws/v2/abcf/Shadowhunters/video/201/ae5f75608d86bf88aa4f9f4aa76ab1b7/579x325-Q100_ae5f75608d86bf88aa4f9f4aa76ab1b7.jpg',
            'duration': 2544,
            'season_number': 2,
            'series': 'Shadowhunters',
            'episode_number': 1,
            'timestamp': 1483387200,
            'ext': 'mp4'
        },
        'params': {
            'geo_bypass_ip_block': '3.244.239.0/24',
            # m3u8 download
            'skip_download': True,
        },
    }, {
        'url': 'https://abc.com/shows/the-rookie/episode-guide/season-04/12-the-knock',
        'info_dict': {
            'id': 'VDKA26050359',
            'title': 'The Knock',
            'description': 'md5:0c2947e3ada4c31f28296db7db14aa64',
            'age_limit': 14,
            'ext': 'mp4',
            'thumbnail': 'http://cdn1.edgedatg.com/aws/v2/abc/TheRookie/video/412/daf830d06e83b11eaf5c0a299d993ae3/1556x876-Q75_daf830d06e83b11eaf5c0a299d993ae3.jpg',
            'episode': 'Episode 12',
            'season_number': 4,
            'season': 'Season 4',
            'timestamp': 1642975200,
            'episode_number': 12,
            'upload_date': '20220123',
            'series': 'The Rookie',
            'duration': 2572,
        },
        'params': {
            'geo_bypass_ip_block': '3.244.239.0/24',
            # m3u8 download
            'skip_download': True,
        },
    }, {
        'url': 'https://fxnow.fxnetworks.com/shows/better-things/video/vdka12782841',
        'info_dict': {
            'id': 'VDKA12782841',
            'title': 'First Look: Better Things - Season 2',
            'description': 'md5:fa73584a95761c605d9d54904e35b407',
            'ext': 'mp4',
            'age_limit': 14,
            'upload_date': '20170825',
            'duration': 161,
            'series': 'Better Things',
            'thumbnail': 'http://cdn1.edgedatg.com/aws/v2/fx/BetterThings/video/12782841/b6b05e58264121cc2c98811318e6d507/1556x876-Q75_b6b05e58264121cc2c98811318e6d507.jpg',
            'timestamp': 1503661074,
        },
        'params': {
            'geo_bypass_ip_block': '3.244.239.0/24',
            # m3u8 download
            'skip_download': True,
        },
    }, {
        'url': 'http://abc.go.com/shows/the-catch/episode-guide/season-01/10-the-wedding',
        'only_matching': True,
    }, {
        'url': 'http://abc.go.com/shows/world-news-tonight/episode-guide/2017-02/17-021717-intense-stand-off-between-man-with-rifle-and-police-in-oakland',
        'only_matching': True,
    }, {
        # brand 004
        'url': 'http://disneynow.go.com/shows/big-hero-6-the-series/season-01/episode-10-mr-sparkles-loses-his-sparkle/vdka4637915',
        'only_matching': True,
    }, {
        # brand 008
        'url': 'http://disneynow.go.com/shows/minnies-bow-toons/video/happy-campers/vdka4872013',
        'only_matching': True,
    }, {
        'url': 'https://disneynow.com/shows/minnies-bow-toons/video/happy-campers/vdka4872013',
        'only_matching': True,
    }, {
        'url': 'https://www.freeform.com/shows/cruel-summer/episode-guide/season-01/01-happy-birthday-jeanette-turner',
        'only_matching': True,
    }]

    def _extract_videos(self, brand, video_id='-1', show_id='-1'):
        display_id = video_id if video_id != '-1' else show_id
        return self._download_json(
            'http://api.contents.watchabc.go.com/vp2/ws/contents/3000/videos/%s/001/-1/%s/-1/%s/-1/-1.json' % (brand, show_id, video_id),
            display_id)['video']

    def _real_extract(self, url):
        mobj = self._match_valid_url(url)
        sub_domain = remove_start(remove_end(mobj.group('sub_domain') or '', '.go'), 'www.')
        video_id, display_id = mobj.group('id', 'display_id')
        site_info = self._SITE_INFO.get(sub_domain, {})
        brand = site_info.get('brand')
        if not video_id or not site_info:
            webpage = self._download_webpage(url, display_id or video_id)
            data = self._parse_json(
                self._search_regex(
                    r'["\']__abc_com__["\']\s*\]\s*=\s*({.+?})\s*;', webpage,
                    'data', default='{}'),
                display_id or video_id, fatal=False)
            # https://abc.com/shows/modern-family/episode-guide/season-01/101-pilot
            layout = try_get(data, lambda x: x['page']['content']['video']['layout'], dict)
            video_id = None
            if layout:
                video_id = try_get(
                    layout,
                    (lambda x: x['videoid'], lambda x: x['video']['id']),
                    compat_str)
            if not video_id:
                video_id = self._search_regex(
                    (
                        # There may be inner quotes, e.g. data-video-id="'VDKA3609139'"
                        # from http://freeform.go.com/shows/shadowhunters/episodes/season-2/1-this-guilty-blood
                        r'data-video-id=["\']*(VDKA\w+)',
                        # page.analytics.videoIdCode
                        r'\bvideoIdCode["\']\s*:\s*["\']((?:vdka|VDKA)\w+)',
                        # https://abc.com/shows/the-rookie/episode-guide/season-02/03-the-bet
                        r'\b(?:video)?id["\']\s*:\s*["\'](VDKA\w+)'
                    ), webpage, 'video id', default=video_id)
            if not site_info:
                brand = self._search_regex(
                    (r'data-brand=\s*["\']\s*(\d+)',
                     r'data-page-brand=\s*["\']\s*(\d+)'), webpage, 'brand',
                    default='004')
                site_info = next(
                    si for _, si in self._SITE_INFO.items()
                    if si.get('brand') == brand)
            if not video_id:
                # show extraction works for Disney, DisneyJunior and DisneyXD
                # ABC and Freeform has different layout
                show_id = self._search_regex(r'data-show-id=["\']*(SH\d+)', webpage, 'show id')
                videos = self._extract_videos(brand, show_id=show_id)
                show_title = self._search_regex(r'data-show-title="([^"]+)"', webpage, 'show title', fatal=False)
                entries = []
                for video in videos:
                    entries.append(self.url_result(
                        video['url'], 'Go', video.get('id'), video.get('title')))
                entries.reverse()
                return self.playlist_result(entries, show_id, show_title)
        video_data = self._extract_videos(brand, video_id)[0]
        video_id = video_data['id']
        title = video_data['title']

        formats = []
        subtitles = {}
        for asset in video_data.get('assets', {}).get('asset', []):
            asset_url = asset.get('value')
            if not asset_url:
                continue
            format_id = asset.get('format')
            ext = determine_ext(asset_url)
            if ext == 'm3u8':
                video_type = video_data.get('type')
                data = {
                    'video_id': video_data['id'],
                    'video_type': video_type,
                    'brand': brand,
                    'device': '001',
                }
                if video_data.get('accesslevel') == '1':
                    requestor_id = site_info.get('requestor_id', 'DisneyChannels')
                    resource = site_info.get('resource_id') or self._get_mvpd_resource(
                        requestor_id, title, video_id, None)
                    auth = self._extract_mvpd_auth(
                        url, video_id, requestor_id, resource)
                    data.update({
                        'token': auth,
                        'token_type': 'ap',
                        'adobe_requestor_id': requestor_id,
                    })
                else:
                    self._initialize_geo_bypass({'countries': ['US']})
                entitlement = self._download_json(
                    'https://api.entitlement.watchabc.go.com/vp2/ws-secure/entitlement/2020/authorize.json',
                    video_id, data=urlencode_postdata(data))
                errors = entitlement.get('errors', {}).get('errors', [])
                if errors:
                    for error in errors:
                        if error.get('code') == 1002:
                            self.raise_geo_restricted(
                                error['message'], countries=['US'])
                    error_message = ', '.join([error['message'] for error in errors])
                    raise ExtractorError('%s said: %s' % (self.IE_NAME, error_message), expected=True)
                asset_url += '?' + entitlement['uplynkData']['sessionKey']
                fmts, subs = self._extract_m3u8_formats_and_subtitles(
                    asset_url, video_id, 'mp4', m3u8_id=format_id or 'hls', fatal=False)
                formats.extend(fmts)
                self._merge_subtitles(subs, target=subtitles)
            else:
                f = {
                    'format_id': format_id,
                    'url': asset_url,
                    'ext': ext,
                }
                if re.search(r'(?:/mp4/source/|_source\.mp4)', asset_url):
                    f.update({
                        'format_id': ('%s-' % format_id if format_id else '') + 'SOURCE',
                        'quality': 1,
                    })
                else:
                    mobj = re.search(r'/(\d+)x(\d+)/', asset_url)
                    if mobj:
                        height = int(mobj.group(2))
                        f.update({
                            'format_id': ('%s-' % format_id if format_id else '') + '%dP' % height,
                            'width': int(mobj.group(1)),
                            'height': height,
                        })
                formats.append(f)
        self._sort_formats(formats)

        for cc in video_data.get('closedcaption', {}).get('src', []):
            cc_url = cc.get('value')
            if not cc_url:
                continue
            ext = determine_ext(cc_url)
            if ext == 'xml':
                ext = 'ttml'
            subtitles.setdefault(cc.get('lang'), []).append({
                'url': cc_url,
                'ext': ext,
            })

        thumbnails = []
        for thumbnail in video_data.get('thumbnails', {}).get('thumbnail', []):
            thumbnail_url = thumbnail.get('value')
            if not thumbnail_url:
                continue
            thumbnails.append({
                'url': thumbnail_url,
                'width': int_or_none(thumbnail.get('width')),
                'height': int_or_none(thumbnail.get('height')),
            })

        return {
            'id': video_id,
            'title': title,
            'description': video_data.get('longdescription') or video_data.get('description'),
            'duration': int_or_none(video_data.get('duration', {}).get('value'), 1000),
            'age_limit': parse_age_limit(video_data.get('tvrating', {}).get('rating')),
            'episode_number': int_or_none(video_data.get('episodenumber')),
            'series': video_data.get('show', {}).get('title'),
            'season_number': int_or_none(video_data.get('season', {}).get('num')),
            'thumbnails': thumbnails,
            'formats': formats,
            'subtitles': subtitles,
            'timestamp': unified_timestamp(traverse_obj(video_data, ('airdates', 'airdate', 0))),
        }


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

class GodTubeIE(InfoExtractor):
    _VALID_URL = r'https?://(?:www\.)?godtube\.com/watch/\?v=(?P<id>[\da-zA-Z]+)'
    _TESTS = [
        {
            'url': 'https://www.godtube.com/watch/?v=0C0CNNNU',
            'md5': '77108c1e4ab58f48031101a1a2119789',
            'info_dict': {
                'id': '0C0CNNNU',
                'ext': 'mp4',
                'title': 'Woman at the well.',
                'duration': 159,
                'timestamp': 1205712000,
                'uploader': 'beverlybmusic',
                'upload_date': '20080317',
                'thumbnail': r're:^https?://.*\.jpg$',
            },
        },
    ]

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

        config = self._download_xml(
            'http://www.godtube.com/resource/mediaplayer/%s.xml' % video_id.lower(),
            video_id, 'Downloading player config XML')

        video_url = config.find('file').text
        uploader = config.find('author').text
        timestamp = parse_iso8601(config.find('date').text)
        duration = parse_duration(config.find('duration').text)
        thumbnail = config.find('image').text

        media = self._download_xml(
            'http://www.godtube.com/media/xml/?v=%s' % video_id, video_id, 'Downloading media XML')

        title = media.find('title').text

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


code:
import hashlib

from .common import InfoExtractor
from ..utils import (
    ExtractorError,
    try_get
)

class GofileIE(InfoExtractor):
    _VALID_URL = r'https?://(?:www\.)?gofile\.io/d/(?P<id>[^/]+)'
    _TESTS = [{
        'url': 'https://gofile.io/d/AMZyDw',
        'info_dict': {
            'id': 'AMZyDw',
        },
        'playlist_mincount': 2,
        'playlist': [{
            'info_dict': {
                'id': 'de571ac1-5edc-42e2-8ec2-bdac83ad4a31',
                'filesize': 928116,
                'ext': 'mp4',
                'title': 'nuuh',
                'release_timestamp': 1638338704,
                'release_date': '20211201',
            }
        }]
    }, {
        'url': 'https://gofile.io/d/is8lKr',
        'info_dict': {
            'id': 'TMjXd9',
            'ext': 'mp4',
        },
        'playlist_count': 0,
        'skip': 'No video/audio found at provided URL.',
    }, {
        'url': 'https://gofile.io/d/TMjXd9',
        'info_dict': {
            'id': 'TMjXd9',
        },
        'playlist_count': 1,
    }, {
        'url': 'https://gofile.io/d/gqOtRf',
        'info_dict': {
            'id': 'gqOtRf',
        },
        'playlist_mincount': 1,
        'params': {
            'videopassword': 'password',
        },
    }]
    _TOKEN = None

    def _real_initialize(self):
        token = self._get_cookies('https://gofile.io/').get('accountToken')
        if token:
            self._TOKEN = token.value
            return

        account_data = self._download_json(
            'https://api.gofile.io/createAccount', None, note='Getting a new guest account')
        self._TOKEN = account_data['data']['token']
        self._set_cookie('gofile.io', 'accountToken', self._TOKEN)

    def _entries(self, file_id):
        query_params = {
            'contentId': file_id,
            'token': self._TOKEN,
            'websiteToken': 12345,
        }
        password = self.get_param('videopassword')
        if password:
            query_params['password'] = hashlib.sha256(password.encode('utf-8')).hexdigest()
        files = self._download_json(
            'https://api.gofile.io/getContent', file_id, note='Getting filelist', query=query_params)

        status = files['status']
        if status == 'error-passwordRequired':
            raise ExtractorError(
                'This video is protected by a password, use the --video-password option', expected=True)
        elif status != 'ok':
            raise ExtractorError(f'{self.IE_NAME} said: status {status}', expected=True)

        found_files = False
        for file in (try_get(files, lambda x: x['data']['contents'], dict) or {}).values():
            file_type, file_format = file.get('mimetype').split('/', 1)
            if file_type not in ('video', 'audio') and file_format != 'vnd.mts':
                continue

            found_files = True
            file_url = file.get('link')
            if file_url:
                yield {
                    'id': file['id'],
                    'title': file['name'].rsplit('.', 1)[0],
                    'url': file_url,
                    'filesize': file.get('size'),
                    'release_timestamp': file.get('createTime')
                }

        if not found_files:
            raise ExtractorError('No video/audio found at provided URL.', expected=True)

    def _real_extract(self, url):
        file_id = self._match_id(url)
        return self.playlist_result(self._entries(file_id), playlist_id=file_id)


code:
from .common import InfoExtractor
from ..compat import (
    compat_str,
    compat_urlparse,
)
from ..utils import (
    determine_ext,
)

class GolemIE(InfoExtractor):
    _VALID_URL = r'^https?://video\.golem\.de/.+?/(?P<id>.+?)/'
    _TEST = {
        'url': 'http://video.golem.de/handy/14095/iphone-6-und-6-plus-test.html',
        'md5': 'c1a2c0a3c863319651c7c992c5ee29bf',
        'info_dict': {
            'id': '14095',
            'format_id': 'high',
            'ext': 'mp4',
            'title': 'iPhone 6 und 6 Plus - Test',
            'duration': 300.44,
            'filesize': 65309548,
        }
    }

    _PREFIX = 'http://video.golem.de'

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

        config = self._download_xml(
            'https://video.golem.de/xml/{0}.xml'.format(video_id), video_id)

        info = {
            'id': video_id,
            'title': config.findtext('./title', 'golem'),
            'duration': self._float(config.findtext('./playtime'), 'duration'),
        }

        formats = []
        for e in config:
            url = e.findtext('./url')
            if not url:
                continue

            formats.append({
                'format_id': compat_str(e.tag),
                'url': compat_urlparse.urljoin(self._PREFIX, url),
                'height': self._int(e.get('height'), 'height'),
                'width': self._int(e.get('width'), 'width'),
                'filesize': self._int(e.findtext('filesize'), 'filesize'),
                'ext': determine_ext(e.findtext('./filename')),
            })
        self._sort_formats(formats)
        info['formats'] = formats

        thumbnails = []
        for e in config.findall('.//teaser'):
            url = e.findtext('./url')
            if not url:
                continue
            thumbnails.append({
                'url': compat_urlparse.urljoin(self._PREFIX, url),
                'width': self._int(e.get('width'), 'thumbnail width'),
                'height': self._int(e.get('height'), 'thumbnail height'),
            })
        info['thumbnails'] = thumbnails

        return info


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

class GoodGameIE(InfoExtractor):
    IE_NAME = 'goodgame:stream'
    _VALID_URL = r'https?://goodgame\.ru/channel/(?P<id>\w+)'
    _TESTS = [{
        'url': 'https://goodgame.ru/channel/Pomi/#autoplay',
        'info_dict': {
            'id': 'pomi',
            'ext': 'mp4',
            'title': r're:Reynor vs Special \(1/2,bo3\) Wardi Spring EU \- playoff \(финальный день\) \d{4}-\d{2}-\d{2} \d{2}:\d{2}$',
            'channel_id': '1644',
            'channel': 'Pomi',
            'channel_url': 'https://goodgame.ru/channel/Pomi/',
            'description': 'md5:4a87b775ee7b2b57bdccebe285bbe171',
            'thumbnail': r're:^https?://.*\.jpg$',
            'live_status': 'is_live',
            'view_count': int,
        },
        'params': {'skip_download': 'm3u8'},
        'skip': 'May not be online',
    }]

    def _real_extract(self, url):
        channel_name = self._match_id(url)
        response = self._download_json(f'https://api2.goodgame.ru/v2/streams/{channel_name}', channel_name)
        player_id = response['channel']['gg_player_src']

        formats, subtitles = [], {}
        if response.get('status') == 'Live':
            formats, subtitles = self._extract_m3u8_formats_and_subtitles(
                f'https://hls.goodgame.ru/manifest/{player_id}_master.m3u8',
                channel_name, 'mp4', live=True)
        else:
            self.raise_no_formats('User is offline', expected=True, video_id=channel_name)

        self._sort_formats(formats)
        return {
            'id': player_id,
            'formats': formats,
            'subtitles': subtitles,
            'title': traverse_obj(response, ('channel', 'title')),
            'channel': channel_name,
            'channel_id': str_or_none(traverse_obj(response, ('channel', 'id'))),
            'channel_url': response.get('url'),
            'description': clean_html(traverse_obj(response, ('channel', 'description'))),
            'thumbnail': traverse_obj(response, ('channel', 'thumb')),
            'is_live': bool(formats),
            'view_count': int_or_none(response.get('viewers')),
            'age_limit': 18 if traverse_obj(response, ('channel', 'adult')) else None,
        }


code:
import json

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

class GooglePodcastsBaseIE(InfoExtractor):
    _VALID_URL_BASE = r'https?://podcasts\.google\.com/feed/'

    def _batch_execute(self, func_id, video_id, params):
        return json.loads(self._download_json(
            'https://podcasts.google.com/_/PodcastsUi/data/batchexecute',
            video_id, data=urlencode_postdata({
                'f.req': json.dumps([[[func_id, json.dumps(params), None, '1']]]),
            }), transform_source=lambda x: self._search_regex(r'(?s)(\[.+\])', x, 'data'))[0][2])

    def _extract_episode(self, episode):
        return {
            'id': episode[4][3],
            'title': episode[8],
            'url': clean_podcast_url(episode[13]),
            'thumbnail': episode[2],
            'description': episode[9],
            'creator': try_get(episode, lambda x: x[14]),
            'timestamp': int_or_none(episode[11]),
            'duration': int_or_none(episode[12]),
            'series': episode[1],
        }

class GooglePodcastsIE(GooglePodcastsBaseIE):
    IE_NAME = 'google:podcasts'
    _VALID_URL = GooglePodcastsBaseIE._VALID_URL_BASE + r'(?P<feed_url>[^/]+)/episode/(?P<id>[^/?&#]+)'
    _TEST = {
        'url': 'https://podcasts.google.com/feed/aHR0cHM6Ly9mZWVkcy5ucHIub3JnLzM0NDA5ODUzOS9wb2RjYXN0LnhtbA/episode/MzBlNWRlN2UtOWE4Yy00ODcwLTk2M2MtM2JlMmUyNmViOTRh',
        'md5': 'fa56b2ee8bd0703e27e42d4b104c4766',
        'info_dict': {
            'id': '30e5de7e-9a8c-4870-963c-3be2e26eb94a',
            'ext': 'mp3',
            'title': 'WWDTM New Year 2021',
            'description': 'We say goodbye to 2020 with Christine Baranksi, Doug Jones, Jonna Mendez, and Kellee Edwards.',
            'upload_date': '20210102',
            'timestamp': 1609606800,
            'duration': 2901,
            'series': "Wait Wait... Don't Tell Me!",
        }
    }

    def _real_extract(self, url):
        b64_feed_url, b64_guid = self._match_valid_url(url).groups()
        episode = self._batch_execute(
            'oNjqVe', b64_guid, [b64_feed_url, b64_guid])[1]
        return self._extract_episode(episode)

class GooglePodcastsFeedIE(GooglePodcastsBaseIE):
    IE_NAME = 'google:podcasts:feed'
    _VALID_URL = GooglePodcastsBaseIE._VALID_URL_BASE + r'(?P<id>[^/?&#]+)/?(?:[?#&]|$)'
    _TEST = {
        'url': 'https://podcasts.google.com/feed/aHR0cHM6Ly9mZWVkcy5ucHIub3JnLzM0NDA5ODUzOS9wb2RjYXN0LnhtbA',
        'info_dict': {
            'title': "Wait Wait... Don't Tell Me!",
            'description': "NPR's weekly current events quiz. Have a laugh and test your news knowledge while figuring out what's real and what we've made up.",
        },
        'playlist_mincount': 20,
    }

    def _real_extract(self, url):
        b64_feed_url = self._match_id(url)
        data = self._batch_execute('ncqJEe', b64_feed_url, [b64_feed_url])

        entries = []
        for episode in (try_get(data, lambda x: x[1][0]) or []):
            entries.append(self._extract_episode(episode))

        feed = try_get(data, lambda x: x[3]) or []
        return self.playlist_result(
            entries, playlist_title=try_get(feed, lambda x: x[0]),
            playlist_description=try_get(feed, lambda x: x[2]))


code:
import itertools
import re

from .common import SearchInfoExtractor

class GoogleSearchIE(SearchInfoExtractor):
    IE_DESC = 'Google Video search'
    IE_NAME = 'video.google:search'
    _SEARCH_KEY = 'gvsearch'
    _TESTS = [{
        'url': 'gvsearch15:python language',
        'info_dict': {
            'id': 'python language',
            'title': 'python language',
        },
        'playlist_count': 15,
    }]
    _PAGE_SIZE = 100

    def _search_results(self, query):
        for pagenum in itertools.count():
            webpage = self._download_webpage(
                'http://www.google.com/search', f'gvsearch:{query}',
                note=f'Downloading result page {pagenum + 1}',
                query={
                    'tbm': 'vid',
                    'q': query,
                    'start': pagenum * self._PAGE_SIZE,
                    'num': self._PAGE_SIZE,
                    'hl': 'en',
                })

            for url in re.findall(r'<div[^>]* class="dXiKIc"[^>]*><a href="([^"]+)"', webpage):
                yield self.url_result(url)

            if not re.search(r'id="pnnext"', webpage):
                return


code:
import base64
import binascii
import datetime
import hashlib
import hmac
import json
import os

from .common import InfoExtractor
from ..utils import (
    ExtractorError,
    traverse_obj,
    unescapeHTML,
)

class GoPlayIE(InfoExtractor):
    _VALID_URL = r'https?://(www\.)?goplay\.be/video/([^/]+/[^/]+/|)(?P<display_id>[^/#]+)'

    _NETRC_MACHINE = 'goplay'

    _TESTS = [{
        'url': 'https://www.goplay.be/video/de-container-cup/de-container-cup-s3/de-container-cup-s3-aflevering-2#autoplay',
        'info_dict': {
            'id': '9c4214b8-e55d-4e4b-a446-f015f6c6f811',
            'ext': 'mp4',
            'title': 'S3 - Aflevering 2',
            'series': 'De Container Cup',
            'season': 'Season 3',
            'season_number': 3,
            'episode': 'Episode 2',
            'episode_number': 2,
        },
        'skip': 'This video is only available for registered users'
    }, {
        'url': 'https://www.goplay.be/video/a-family-for-thr-holidays-s1-aflevering-1#autoplay',
        'info_dict': {
            'id': '74e3ed07-748c-49e4-85a0-393a93337dbf',
            'ext': 'mp4',
            'title': 'A Family for the Holidays',
        },
        'skip': 'This video is only available for registered users'
    }]

    _id_token = None

    def _perform_login(self, username, password):
        self.report_login()
        aws = AwsIdp(ie=self, pool_id='eu-west-1_dViSsKM5Y', client_id='6s1h851s8uplco5h6mqh1jac8m')
        self._id_token, _ = aws.authenticate(username=username, password=password)

    def _real_initialize(self):
        if not self._id_token:
            raise self.raise_login_required(method='password')

    def _real_extract(self, url):
        url, display_id = self._match_valid_url(url).group(0, 'display_id')
        webpage = self._download_webpage(url, display_id)
        video_data_json = self._html_search_regex(r'<div\s+data-hero="([^"]+)"', webpage, 'video_data')
        video_data = self._parse_json(unescapeHTML(video_data_json), display_id).get('data')

        movie = video_data.get('movie')
        if movie:
            video_id = movie['videoUuid']
            info_dict = {
                'title': movie.get('title')
            }
        else:
            episode = traverse_obj(video_data, ('playlists', ..., 'episodes', lambda _, v: v['pageInfo']['url'] == url), get_all=False)
            video_id = episode['videoUuid']
            info_dict = {
                'title': episode.get('episodeTitle'),
                'series': traverse_obj(episode, ('program', 'title')),
                'season_number': episode.get('seasonNumber'),
                'episode_number': episode.get('episodeNumber'),
            }

        api = self._download_json(
            f'https://api.viervijfzes.be/content/{video_id}',
            video_id, headers={'Authorization': self._id_token})

        formats, subs = self._extract_m3u8_formats_and_subtitles(
            api['video']['S'], video_id, ext='mp4', m3u8_id='HLS')
        self._sort_formats(formats)

        info_dict.update({
            'id': video_id,
            'formats': formats,
        })

        return info_dict

# Taken from https://github.com/add-ons/plugin.video.viervijfzes/blob/master/resources/lib/viervijfzes/auth_awsidp.py
# Released into Public domain by https://github.com/michaelarnauts

class InvalidLoginException(ExtractorError):
    """ The login credentials are invalid """

class AuthenticationException(ExtractorError):
    """ Something went wrong while logging in """

class AwsIdp:
    """ AWS Identity Provider """

    def __init__(self, ie, pool_id, client_id):
        """
        :param InfoExtrator ie: The extractor that instantiated this class.
        :param str pool_id:     The AWS user pool to connect to (format: <region>_<poolid>).
                                E.g.: eu-west-1_aLkOfYN3T
        :param str client_id:   The client application ID (the ID of the application connecting)
        """

        self.ie = ie

        self.pool_id = pool_id
        if "_" not in self.pool_id:
            raise ValueError("Invalid pool_id format. Should be <region>_<poolid>.")

        self.client_id = client_id
        self.region = self.pool_id.split("_")[0]
        self.url = "https://cognito-idp.%s.amazonaws.com/" % (self.region,)

        # Initialize the values
        # https://github.com/aws/amazon-cognito-identity-js/blob/master/src/AuthenticationHelper.js#L22
        self.n_hex = 'FFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD1' + \
                     '29024E088A67CC74020BBEA63B139B22514A08798E3404DD' + \
                     'EF9519B3CD3A431B302B0A6DF25F14374FE1356D6D51C245' + \
                     'E485B576625E7EC6F44C42E9A637ED6B0BFF5CB6F406B7ED' + \
                     'EE386BFB5A899FA5AE9F24117C4B1FE649286651ECE45B3D' + \
                     'C2007CB8A163BF0598DA48361C55D39A69163FA8FD24CF5F' + \
                     '83655D23DCA3AD961C62F356208552BB9ED529077096966D' + \
                     '670C354E4ABC9804F1746C08CA18217C32905E462E36CE3B' + \
                     'E39E772C180E86039B2783A2EC07A28FB5C55DF06F4C52C9' + \
                     'DE2BCBF6955817183995497CEA956AE515D2261898FA0510' + \
                     '15728E5A8AAAC42DAD33170D04507A33A85521ABDF1CBA64' + \
                     'ECFB850458DBEF0A8AEA71575D060C7DB3970F85A6E1E4C7' + \
                     'ABF5AE8CDB0933D71E8C94E04A25619DCEE3D2261AD2EE6B' + \
                     'F12FFA06D98A0864D87602733EC86A64521F2B18177B200C' + \
                     'BBE117577A615D6C770988C0BAD946E208E24FA074E5AB31' + \
                     '43DB5BFCE0FD108E4B82D120A93AD2CAFFFFFFFFFFFFFFFF'

        # https://github.com/aws/amazon-cognito-identity-js/blob/master/src/AuthenticationHelper.js#L49
        self.g_hex = '2'
        self.info_bits = bytearray('Caldera Derived Key', 'utf-8')

        self.big_n = self.__hex_to_long(self.n_hex)
        self.g = self.__hex_to_long(self.g_hex)
        self.k = self.__hex_to_long(self.__hex_hash('00' + self.n_hex + '0' + self.g_hex))
        self.small_a_value = self.__generate_random_small_a()
        self.large_a_value = self.__calculate_a()

    def authenticate(self, username, password):
        """ Authenticate with a username and password. """
        # Step 1: First initiate an authentication request
        auth_data_dict = self.__get_authentication_request(username)
        auth_data = json.dumps(auth_data_dict).encode("utf-8")
        auth_headers = {
            "X-Amz-Target": "AWSCognitoIdentityProviderService.InitiateAuth",
            "Accept-Encoding": "identity",
            "Content-Type": "application/x-amz-json-1.1"
        }
        auth_response_json = self.ie._download_json(
            self.url, None, data=auth_data, headers=auth_headers,
            note='Authenticating username', errnote='Invalid username')
        challenge_parameters = auth_response_json.get("ChallengeParameters")

        if auth_response_json.get("ChallengeName") != "PASSWORD_VERIFIER":
            raise AuthenticationException(auth_response_json["message"])

        # Step 2: Respond to the Challenge with a valid ChallengeResponse
        challenge_request = self.__get_challenge_response_request(challenge_parameters, password)
        challenge_data = json.dumps(challenge_request).encode("utf-8")
        challenge_headers = {
            "X-Amz-Target": "AWSCognitoIdentityProviderService.RespondToAuthChallenge",
            "Content-Type": "application/x-amz-json-1.1"
        }
        auth_response_json = self.ie._download_json(
            self.url, None, data=challenge_data, headers=challenge_headers,
            note='Authenticating password', errnote='Invalid password')

        if 'message' in auth_response_json:
            raise InvalidLoginException(auth_response_json['message'])
        return (
            auth_response_json['AuthenticationResult']['IdToken'],
            auth_response_json['AuthenticationResult']['RefreshToken']
        )

    def __get_authentication_request(self, username):
        """

        :param str username:    The username to use

        :return: A full Authorization request.
        :rtype: dict
        """
        auth_request = {
            "AuthParameters": {
                "USERNAME": username,
                "SRP_A": self.__long_to_hex(self.large_a_value)
            },
            "AuthFlow": "USER_SRP_AUTH",
            "ClientId": self.client_id
        }
        return auth_request

    def __get_challenge_response_request(self, challenge_parameters, password):
        """ Create a Challenge Response Request object.

        :param dict[str,str|imt] challenge_parameters:  The parameters for the challenge.
        :param str password:                            The password.

        :return: A valid and full request data object to use as a response for a challenge.
        :rtype: dict
        """
        user_id = challenge_parameters["USERNAME"]
        user_id_for_srp = challenge_parameters["USER_ID_FOR_SRP"]
        srp_b = challenge_parameters["SRP_B"]
        salt = challenge_parameters["SALT"]
        secret_block = challenge_parameters["SECRET_BLOCK"]

        timestamp = self.__get_current_timestamp()

        # Get a HKDF key for the password, SrpB and the Salt
        hkdf = self.__get_hkdf_key_for_password(
            user_id_for_srp,
            password,
            self.__hex_to_long(srp_b),
            salt
        )
        secret_block_bytes = base64.standard_b64decode(secret_block)

        # the message is a combo of the pool_id, provided SRP userId, the Secret and Timestamp
        msg = \
            bytearray(self.pool_id.split('_')[1], 'utf-8') + \
            bytearray(user_id_for_srp, 'utf-8') + \
            bytearray(secret_block_bytes) + \
            bytearray(timestamp, 'utf-8')
        hmac_obj = hmac.new(hkdf, msg, digestmod=hashlib.sha256)
        signature_string = base64.standard_b64encode(hmac_obj.digest()).decode('utf-8')
        challenge_request = {
            "ChallengeResponses": {
                "USERNAME": user_id,
                "TIMESTAMP": timestamp,
                "PASSWORD_CLAIM_SECRET_BLOCK": secret_block,
                "PASSWORD_CLAIM_SIGNATURE": signature_string
            },
            "ChallengeName": "PASSWORD_VERIFIER",
            "ClientId": self.client_id
        }
        return challenge_request

    def __get_hkdf_key_for_password(self, username, password, server_b_value, salt):
        """ Calculates the final hkdf based on computed S value, and computed U value and the key.

        :param str username:        Username.
        :param str password:        Password.
        :param int server_b_value:  Server B value.
        :param int salt:            Generated salt.

        :return Computed HKDF value.
        :rtype: object
        """

        u_value = self.__calculate_u(self.large_a_value, server_b_value)
        if u_value == 0:
            raise ValueError('U cannot be zero.')
        username_password = '%s%s:%s' % (self.pool_id.split('_')[1], username, password)
        username_password_hash = self.__hash_sha256(username_password.encode('utf-8'))

        x_value = self.__hex_to_long(self.__hex_hash(self.__pad_hex(salt) + username_password_hash))
        g_mod_pow_xn = pow(self.g, x_value, self.big_n)
        int_value2 = server_b_value - self.k * g_mod_pow_xn
        s_value = pow(int_value2, self.small_a_value + u_value * x_value, self.big_n)
        hkdf = self.__compute_hkdf(
            bytearray.fromhex(self.__pad_hex(s_value)),
            bytearray.fromhex(self.__pad_hex(self.__long_to_hex(u_value)))
        )
        return hkdf

    def __compute_hkdf(self, ikm, salt):
        """ Standard hkdf algorithm

        :param {Buffer} ikm Input key material.
        :param {Buffer} salt Salt value.
        :return {Buffer} Strong key material.
        """

        prk = hmac.new(salt, ikm, hashlib.sha256).digest()
        info_bits_update = self.info_bits + bytearray(chr(1), 'utf-8')
        hmac_hash = hmac.new(prk, info_bits_update, hashlib.sha256).digest()
        return hmac_hash[:16]

    def __calculate_u(self, big_a, big_b):
        """ Calculate the client's value U which is the hash of A and B

        :param int big_a:   Large A value.
        :param int big_b:   Server B value.

        :return Computed U value.
        :rtype: int
        """

        u_hex_hash = self.__hex_hash(self.__pad_hex(big_a) + self.__pad_hex(big_b))
        return self.__hex_to_long(u_hex_hash)

    def __generate_random_small_a(self):
        """ Helper function to generate a random big integer

        :return a random value.
        :rtype: int
        """
        random_long_int = self.__get_random(128)
        return random_long_int % self.big_n

    def __calculate_a(self):
        """ Calculate the client's public value A = g^a%N with the generated random number a

        :return Computed large A.
        :rtype: int
        """

        big_a = pow(self.g, self.small_a_value, self.big_n)
        # safety check
        if (big_a % self.big_n) == 0:
            raise ValueError('Safety check for A failed')
        return big_a

    @staticmethod
    def __long_to_hex(long_num):
        return '%x' % long_num

    @staticmethod
    def __hex_to_long(hex_string):
        return int(hex_string, 16)

    @staticmethod
    def __hex_hash(hex_string):
        return AwsIdp.__hash_sha256(bytearray.fromhex(hex_string))

    @staticmethod
    def __hash_sha256(buf):
        """AuthenticationHelper.hash"""
        digest = hashlib.sha256(buf).hexdigest()
        return (64 - len(digest)) * '0' + digest

    @staticmethod
    def __pad_hex(long_int):
        """ Converts a Long integer (or hex string) to hex format padded with zeroes for hashing

        :param int|str long_int:    Number or string to pad.

        :return Padded hex string.
        :rtype: str
        """

        if not isinstance(long_int, str):
            hash_str = AwsIdp.__long_to_hex(long_int)
        else:
            hash_str = long_int
        if len(hash_str) % 2 == 1:
            hash_str = '0%s' % hash_str
        elif hash_str[0] in '89ABCDEFabcdef':
            hash_str = '00%s' % hash_str
        return hash_str

    @staticmethod
    def __get_random(nbytes):
        random_hex = binascii.hexlify(os.urandom(nbytes))
        return AwsIdp.__hex_to_long(random_hex)

    @staticmethod
    def __get_current_timestamp():
        """ Creates a timestamp with the correct English format.

        :return: timestamp in format 'Sun Jan 27 19:00:04 UTC 2019'
        :rtype: str
        """

        # We need US only data, so we cannot just do a strftime:
        # Sun Jan 27 19:00:04 UTC 2019
        months = [None, 'Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec']
        days = ['Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun']

        time_now = datetime.datetime.utcnow()
        format_string = "{} {} {} %H:%M:%S UTC %Y".format(days[time_now.weekday()], months[time_now.month], time_now.day)
        time_string = datetime.datetime.utcnow().strftime(format_string)
        return time_string

    def __str__(self):
        return "AWS IDP Client for:\nRegion: %s\nPoolId: %s\nAppId:  %s" % (
            self.region, self.pool_id.split("_")[1], self.client_id
        )


code:
from .common import InfoExtractor
from ..utils import (
    int_or_none,
    remove_end,
    str_or_none,
    try_get,
    unified_timestamp,
    url_or_none,
)

class GoProIE(InfoExtractor):
    _VALID_URL = r'https?://(www\.)?gopro\.com/v/(?P<id>[A-Za-z0-9]+)'

    _TESTS = [{
        'url': 'https://gopro.com/v/ZNVvED8QDzR5V',
        'info_dict': {
            'id': 'ZNVvED8QDzR5V',
            'title': 'My GoPro Adventure - 9/19/21',
            'thumbnail': r're:https?://.+',
            'ext': 'mp4',
            'timestamp': 1632072947,
            'upload_date': '20210919',
            'uploader_id': 'fireydive30018',
            'duration': 396062,
        }
    }, {
        'url': 'https://gopro.com/v/KRm6Vgp2peg4e',
        'info_dict': {
            'id': 'KRm6Vgp2peg4e',
            'title': 'じゃがいも カリカリ オーブン焼き',
            'thumbnail': r're:https?://.+',
            'ext': 'mp4',
            'timestamp': 1607231125,
            'upload_date': '20201206',
            'uploader_id': 'dc9bcb8b-47d2-47c6-afbc-4c48f9a3769e',
            'duration': 45187,
            'track': 'The Sky Machine',
        }
    }, {
        'url': 'https://gopro.com/v/kVrK9wlJvBMwn',
        'info_dict': {
            'id': 'kVrK9wlJvBMwn',
            'title': 'DARKNESS',
            'thumbnail': r're:https?://.+',
            'ext': 'mp4',
            'timestamp': 1594183735,
            'upload_date': '20200708',
            'uploader_id': '闇夜乃皇帝',
            'duration': 313075,
            'track': 'Battery (Live)',
            'artist': 'Metallica',
        }
    }]

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

        metadata = self._parse_json(
            self._html_search_regex(r'window\.__reflectData\s*=\s*([^;]+)', webpage, 'metadata'), video_id)

        video_info = metadata['collectionMedia'][0]
        media_data = self._download_json(
            'https://api.gopro.com/media/%s/download' % video_info['id'], video_id)

        formats = []
        for fmt in try_get(media_data, lambda x: x['_embedded']['variations']) or []:
            format_url = url_or_none(fmt.get('url'))
            if not format_url:
                continue
            formats.append({
                'url': format_url,
                'format_id': str_or_none(fmt.get('quality')),
                'format_note': str_or_none(fmt.get('label')),
                'ext': str_or_none(fmt.get('type')),
                'width': int_or_none(fmt.get('width')),
                'height': int_or_none(fmt.get('height')),
            })

        self._sort_formats(formats)

        title = str_or_none(
            try_get(metadata, lambda x: x['collection']['title'])
            or self._html_search_meta(['og:title', 'twitter:title'], webpage)
            or remove_end(self._html_search_regex(
                r'<title[^>]*>([^<]+)</title>', webpage, 'title', fatal=False), ' | GoPro'))
        if title:
            title = title.replace('\n', ' ')

        return {
            'id': video_id,
            'title': title,
            'formats': formats,
            'thumbnail': url_or_none(
                self._html_search_meta(['og:image', 'twitter:image'], webpage)),
            'timestamp': unified_timestamp(
                try_get(metadata, lambda x: x['collection']['created_at'])),
            'uploader_id': str_or_none(
                try_get(metadata, lambda x: x['account']['nickname'])),
            'duration': int_or_none(
                video_info.get('source_duration')),
            'artist': str_or_none(
                video_info.get('music_track_artist')),
            'track': str_or_none(
                video_info.get('music_track_name')),
        }


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

from .common import InfoExtractor
from ..utils import unescapeHTML

class GorinLiveIE(InfoExtractor):
    _VALID_URL = r'https?://(?:www\.)?gorin\.jp/live/(?P<id>[A-Za-z0-9_-]+)'
    _TESTS = [{
        'url': 'https://gorin.jp/live/BSBWSBLTEAM9----------GP--000500--/',
        'only_matching': True,
    }]
    BRIGHTCOVE_URL_TEMPLATE = 'http://players.brightcove.net/%s/default_default/index.html?videoId=%s'

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

        webpage = self._download_webpage(url, video_id)
        rdet = self._search_regex(
            r'data-json="(.+?)"', webpage, 'details', group=1)
        rdet = unescapeHTML(rdet)
        details = self._parse_json(rdet, video_id)
        live = details['live']

        # all uses this account
        p_id = '4774017240001'
        r_id = live['video_id']
        bc_url = self.BRIGHTCOVE_URL_TEMPLATE % (p_id, r_id)

        return {
            '_type': 'url_transparent',
            'title': live['title'],
            'url': bc_url,
            'ie_key': 'BrightcoveNew',
        }

class GorinVideoIE(InfoExtractor):
    _VALID_URL = r'https?://(?:www\.)?gorin\.jp/video/(?P<id>\d+)'
    _TESTS = [{
        'url': 'https://gorin.jp/video/6264589782001/',
        'only_matching': True,
    }]
    BRIGHTCOVE_URL_TEMPLATE = 'http://players.brightcove.net/%s/default_default/index.html?videoId=%s'

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

        webpage = self._download_webpage(url, video_id)
        rdet = self._search_regex(
            r'data-json="(.+?)"', webpage, 'details', group=1)
        rdet = unescapeHTML(rdet)
        details = self._parse_json(rdet, video_id)

        # all uses this account
        p_id = '4774017240001'
        r_id = details.get('video_id') or video_id
        bc_url = self.BRIGHTCOVE_URL_TEMPLATE % (p_id, r_id)

        return {
            '_type': 'url_transparent',
            'url': bc_url,
            'ie_key': 'BrightcoveNew',
        }


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

class GoshgayIE(InfoExtractor):
    _VALID_URL = r'https?://(?:www\.)?goshgay\.com/video(?P<id>\d+?)($|/)'
    _TEST = {
        'url': 'http://www.goshgay.com/video299069/diesel_sfw_xxx_video',
        'md5': '4b6db9a0a333142eb9f15913142b0ed1',
        'info_dict': {
            'id': '299069',
            'ext': 'flv',
            'title': 'DIESEL SFW XXX Video',
            'thumbnail': r're:^http://.*\.jpg$',
            'duration': 80,
            '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'
(.*?)<', webpage, 'title')
        duration = parse_duration(self._html_search_regex(
            r'\s*-?\s*(.*?)',
            webpage, 'duration', fatal=False))

        flashvars = compat_parse_qs(self._html_search_regex(
            r'<embed.+?id="flash-player-embed".+?flashvars="([^"]+)"',
            webpage, 'flashvars'))
        thumbnail = flashvars.get('url_bigthumb', [None])[0]
        video_url = flashvars['flv_url'][0]

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


code:
from .common import InfoExtractor
from ..compat import compat_str
from ..utils import (
    try_get,
    url_or_none
)

import json

class GoToStageIE(InfoExtractor):
    _VALID_URL = r'https?://(?:www\.)?gotostage\.com/channel/[a-z0-9]+/recording/(?P<id>[a-z0-9]+)/watch'
    _TESTS = [{
        'url': 'https://www.gotostage.com/channel/8901680603948959494/recording/60bb55548d434f21b9ce4f0e225c4895/watch',
        'md5': 'ca72ce990cdcd7a2bd152f7217e319a2',
        'info_dict': {
            'id': '60bb55548d434f21b9ce4f0e225c4895',
            'ext': 'mp4',
            'title': 'What is GoToStage?',
            'thumbnail': r're:^https?://.*\.jpg$',
            'duration': 93.924711
        }
    }, {
        'url': 'https://www.gotostage.com/channel/bacc3d3535b34bafacc3f4ef8d4df78a/recording/831e74cd3e0042be96defba627b6f676/watch?source=HOMEPAGE',
        'only_matching': True,
    }]

    def _real_extract(self, url):
        video_id = self._match_id(url)
        metadata = self._download_json(
            'https://api.gotostage.com/contents?ids=%s' % video_id,
            video_id,
            note='Downloading video metadata',
            errnote='Unable to download video metadata')[0]

        registration_data = {
            'product': metadata['product'],
            'resourceType': metadata['contentType'],
            'productReferenceKey': metadata['productRefKey'],
            'firstName': 'foo',
            'lastName': 'bar',
            'email': 'foobar@example.com'
        }

        registration_response = self._download_json(
            'https://api-registrations.logmeininc.com/registrations',
            video_id,
            data=json.dumps(registration_data).encode(),
            expected_status=409,
            headers={'Content-Type': 'application/json'},
            note='Register user',
            errnote='Unable to register user')

        content_response = self._download_json(
            'https://api.gotostage.com/contents/%s/asset' % video_id,
            video_id,
            headers={'x-registrantkey': registration_response['registrationKey']},
            note='Get download url',
            errnote='Unable to get download url')

        return {
            'id': video_id,
            'title': try_get(metadata, lambda x: x['title'], compat_str),
            'url': try_get(content_response, lambda x: x['cdnLocation'], compat_str),
            'ext': 'mp4',
            'thumbnail': url_or_none(try_get(metadata, lambda x: x['thumbnail']['location'])),
            'duration': try_get(metadata, lambda x: x['duration'], float),
            'categories': [try_get(metadata, lambda x: x['category'], compat_str)],
            'is_live': False
        }


code:
from .common import InfoExtractor

class GPUTechConfIE(InfoExtractor):
    _VALID_URL = r'https?://on-demand\.gputechconf\.com/gtc/2015/video/S(?P<id>\d+)\.html'
    _TEST = {
        'url': 'http://on-demand.gputechconf.com/gtc/2015/video/S5156.html',
        'md5': 'a8862a00a0fd65b8b43acc5b8e33f798',
        'info_dict': {
            'id': '5156',
            'ext': 'mp4',
            'title': 'Coordinating More Than 3 Million CUDA Threads for Social Network Analysis',
            'duration': 1219,
        }
    }

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

        root_path = self._search_regex(
            r'var\s+rootPath\s*=\s*"([^"]+)', webpage, 'root path',
            default='http://evt.dispeak.com/nvidia/events/gtc15/')
        xml_file_id = self._search_regex(
            r'var\s+xmlFileId\s*=\s*"([^"]+)', webpage, 'xml file id')

        return {
            '_type': 'url_transparent',
            'id': video_id,
            'url': '%sxml/%s.xml' % (root_path, xml_file_id),
            'ie_key': 'DigitallySpeaking',
        }