code:
import datetime
import hashlib
import re
import time

from .common import InfoExtractor
from ..compat import (
    compat_b64decode,
    compat_ord,
    compat_str,
    compat_urllib_parse_urlencode,
)
from ..utils import (
    determine_ext,
    encode_data_uri,
    ExtractorError,
    int_or_none,
    orderedSet,
    parse_iso8601,
    str_or_none,
    url_basename,
    urshift,
)

class LeIE(InfoExtractor):
    IE_DESC = '乐视网'
    _VALID_URL = r'https?://(?:www\.le\.com/ptv/vplay|(?:sports\.le|(?:www\.)?lesports)\.com/(?:match|video))/(?P<id>\d+)\.html'
    _GEO_COUNTRIES = ['CN']
    _URL_TEMPLATE = 'http://www.le.com/ptv/vplay/%s.html'

    _TESTS = [{
        'url': 'http://www.le.com/ptv/vplay/22005890.html',
        'md5': 'edadcfe5406976f42f9f266057ee5e40',
        'info_dict': {
            'id': '22005890',
            'ext': 'mp4',
            'title': '第87届奥斯卡颁奖礼完美落幕 《鸟人》成最大赢家',
            'description': 'md5:a9cb175fd753e2962176b7beca21a47c',
        },
        'params': {
            'hls_prefer_native': True,
        },
    }, {
        'url': 'http://www.le.com/ptv/vplay/1415246.html',
        'info_dict': {
            'id': '1415246',
            'ext': 'mp4',
            'title': '美人天下01',
            'description': 'md5:28942e650e82ed4fcc8e4de919ee854d',
        },
        'params': {
            'hls_prefer_native': True,
        },
    }, {
        'note': 'This video is available only in Mainland China, thus a proxy is needed',
        'url': 'http://www.le.com/ptv/vplay/1118082.html',
        'md5': '2424c74948a62e5f31988438979c5ad1',
        'info_dict': {
            'id': '1118082',
            'ext': 'mp4',
            'title': '与龙共舞 完整版',
            'description': 'md5:7506a5eeb1722bb9d4068f85024e3986',
        },
        'params': {
            'hls_prefer_native': True,
        },
    }, {
        'url': 'http://sports.le.com/video/25737697.html',
        'only_matching': True,
    }, {
        'url': 'http://www.lesports.com/match/1023203003.html',
        'only_matching': True,
    }, {
        'url': 'http://sports.le.com/match/1023203003.html',
        'only_matching': True,
    }]

    # ror() and calc_time_key() are reversed from a embedded swf file in LetvPlayer.swf
    def ror(self, param1, param2):
        _loc3_ = 0
        while _loc3_ < param2:
            param1 = urshift(param1, 1) + ((param1 & 1) << 31)
            _loc3_ += 1
        return param1

    def calc_time_key(self, param1):
        _loc2_ = 185025305
        return self.ror(param1, _loc2_ % 17) ^ _loc2_

    # see M3U8Encryption class in KLetvPlayer.swf
    @staticmethod
    def decrypt_m3u8(encrypted_data):
        if encrypted_data[:5].decode('utf-8').lower() != 'vc_01':
            return encrypted_data
        encrypted_data = encrypted_data[5:]

        _loc4_ = bytearray(2 * len(encrypted_data))
        for idx, val in enumerate(encrypted_data):
            b = compat_ord(val)
            _loc4_[2 * idx] = b // 16
            _loc4_[2 * idx + 1] = b % 16
        idx = len(_loc4_) - 11
        _loc4_ = _loc4_[idx:] + _loc4_[:idx]
        _loc7_ = bytearray(len(encrypted_data))
        for i in range(len(encrypted_data)):
            _loc7_[i] = _loc4_[2 * i] * 16 + _loc4_[2 * i + 1]

        return bytes(_loc7_)

    def _check_errors(self, play_json):
        # Check for errors
        playstatus = play_json['msgs']['playstatus']
        if playstatus['status'] == 0:
            flag = playstatus['flag']
            if flag == 1:
                self.raise_geo_restricted()
            else:
                raise ExtractorError('Generic error. flag = %d' % flag, expected=True)

    def _real_extract(self, url):
        media_id = self._match_id(url)
        page = self._download_webpage(url, media_id)

        play_json_flash = self._download_json(
            'http://player-pc.le.com/mms/out/video/playJson',
            media_id, 'Downloading flash playJson data', query={
                'id': media_id,
                'platid': 1,
                'splatid': 105,
                'format': 1,
                'source': 1000,
                'tkey': self.calc_time_key(int(time.time())),
                'domain': 'www.le.com',
                'region': 'cn',
            },
            headers=self.geo_verification_headers())
        self._check_errors(play_json_flash)

        def get_flash_urls(media_url, format_id):
            nodes_data = self._download_json(
                media_url, media_id,
                'Download JSON metadata for format %s' % format_id,
                query={
                    'm3v': 1,
                    'format': 1,
                    'expect': 3,
                    'tss': 'ios',
                })

            req = self._request_webpage(
                nodes_data['nodelist'][0]['location'], media_id,
                note='Downloading m3u8 information for format %s' % format_id)

            m3u8_data = self.decrypt_m3u8(req.read())

            return {
                'hls': encode_data_uri(m3u8_data, 'application/vnd.apple.mpegurl'),
            }

        extracted_formats = []
        formats = []
        playurl = play_json_flash['msgs']['playurl']
        play_domain = playurl['domain'][0]

        for format_id, format_data in playurl.get('dispatch', []).items():
            if format_id in extracted_formats:
                continue
            extracted_formats.append(format_id)

            media_url = play_domain + format_data[0]
            for protocol, format_url in get_flash_urls(media_url, format_id).items():
                f = {
                    'url': format_url,
                    'ext': determine_ext(format_data[1]),
                    'format_id': '%s-%s' % (protocol, format_id),
                    'protocol': 'm3u8_native' if protocol == 'hls' else 'http',
                    'quality': int_or_none(format_id),
                }

                if format_id[-1:] == 'p':
                    f['height'] = int_or_none(format_id[:-1])

                formats.append(f)
        self._sort_formats(formats, ('res', 'quality'))

        publish_time = parse_iso8601(self._html_search_regex(
            r'发布时间 ([^<>]+) ', page, 'publish time', default=None),
            delimiter=' ', timezone=datetime.timedelta(hours=8))
        description = self._html_search_meta('description', page, fatal=False)

        return {
            'id': media_id,
            'formats': formats,
            'title': playurl['title'],
            'thumbnail': playurl['pic'],
            'description': description,
            'timestamp': publish_time,
        }

class LePlaylistIE(InfoExtractor):
    _VALID_URL = r'https?://[a-z]+\.le\.com/(?!video)[a-z]+/(?P<id>[a-z0-9_]+)'

    _TESTS = [{
        'url': 'http://www.le.com/tv/46177.html',
        'info_dict': {
            'id': '46177',
            'title': '美人天下',
            'description': 'md5:395666ff41b44080396e59570dbac01c'
        },
        'playlist_count': 35
    }, {
        'url': 'http://tv.le.com/izt/wuzetian/index.html',
        'info_dict': {
            'id': 'wuzetian',
            'title': '武媚娘传奇',
            'description': 'md5:e12499475ab3d50219e5bba00b3cb248'
        },
        # This playlist contains some extra videos other than the drama itself
        'playlist_mincount': 96
    }, {
        'url': 'http://tv.le.com/pzt/lswjzzjc/index.shtml',
        # This series is moved to http://www.le.com/tv/10005297.html
        'only_matching': True,
    }, {
        'url': 'http://www.le.com/comic/92063.html',
        'only_matching': True,
    }, {
        'url': 'http://list.le.com/listn/c1009_sc532002_d2_p1_o1.html',
        'only_matching': True,
    }]

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

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

        # Currently old domain names are still used in playlists
        media_ids = orderedSet(re.findall(
            r'<a[^>]+href="http://www\.letv\.com/ptv/vplay/(\d+)\.html', page))
        entries = [self.url_result(LeIE._URL_TEMPLATE % media_id, ie='Le')
                   for media_id in media_ids]

        title = self._html_search_meta('keywords', page,
                                       fatal=False).split('，')[0]
        description = self._html_search_meta('description', page, fatal=False)

        return self.playlist_result(entries, playlist_id, playlist_title=title,
                                    playlist_description=description)

class LetvCloudIE(InfoExtractor):
    # Most of *.letv.com is changed to *.le.com on 2016/01/02
    # but yuntv.letv.com is kept, so also keep the extractor name
    IE_DESC = '乐视云'
    _VALID_URL = r'https?://yuntv\.letv\.com/bcloud.html\?.+'

    _TESTS = [{
        'url': 'http://yuntv.letv.com/bcloud.html?uu=p7jnfw5hw9&vu=467623dedf',
        'md5': '26450599afd64c513bc77030ad15db44',
        'info_dict': {
            'id': 'p7jnfw5hw9_467623dedf',
            'ext': 'mp4',
            'title': 'Video p7jnfw5hw9_467623dedf',
        },
    }, {
        'url': 'http://yuntv.letv.com/bcloud.html?uu=p7jnfw5hw9&vu=ec93197892&pu=2c7cd40209&auto_play=1&gpcflag=1&width=640&height=360',
        'md5': 'e03d9cc8d9c13191e1caf277e42dbd31',
        'info_dict': {
            'id': 'p7jnfw5hw9_ec93197892',
            'ext': 'mp4',
            'title': 'Video p7jnfw5hw9_ec93197892',
        },
    }, {
        'url': 'http://yuntv.letv.com/bcloud.html?uu=p7jnfw5hw9&vu=187060b6fd',
        'md5': 'cb988699a776b22d4a41b9d43acfb3ac',
        'info_dict': {
            'id': 'p7jnfw5hw9_187060b6fd',
            'ext': 'mp4',
            'title': 'Video p7jnfw5hw9_187060b6fd',
        },
    }]

    @staticmethod
    def sign_data(obj):
        if obj['cf'] == 'flash':
            salt = '2f9d6924b33a165a6d8b5d3d42f4f987'
            items = ['cf', 'format', 'ran', 'uu', 'ver', 'vu']
        elif obj['cf'] == 'html5':
            salt = 'fbeh5player12c43eccf2bec3300344'
            items = ['cf', 'ran', 'uu', 'bver', 'vu']
        input_data = ''.join([item + obj[item] for item in items]) + salt
        obj['sign'] = hashlib.md5(input_data.encode('utf-8')).hexdigest()

    def _get_formats(self, cf, uu, vu, media_id):
        def get_play_json(cf, timestamp):
            data = {
                'cf': cf,
                'ver': '2.2',
                'bver': 'firefox44.0',
                'format': 'json',
                'uu': uu,
                'vu': vu,
                'ran': compat_str(timestamp),
            }
            self.sign_data(data)
            return self._download_json(
                'http://api.letvcloud.com/gpc.php?' + compat_urllib_parse_urlencode(data),
                media_id, 'Downloading playJson data for type %s' % cf)

        play_json = get_play_json(cf, time.time())
        # The server time may be different from local time
        if play_json.get('code') == 10071:
            play_json = get_play_json(cf, play_json['timestamp'])

        if not play_json.get('data'):
            if play_json.get('message'):
                raise ExtractorError('Letv cloud said: %s' % play_json['message'], expected=True)
            elif play_json.get('code'):
                raise ExtractorError('Letv cloud returned error %d' % play_json['code'], expected=True)
            else:
                raise ExtractorError('Letv cloud returned an unknown error')

        def b64decode(s):
            return compat_b64decode(s).decode('utf-8')

        formats = []
        for media in play_json['data']['video_info']['media'].values():
            play_url = media['play_url']
            url = b64decode(play_url['main_url'])
            decoded_url = b64decode(url_basename(url))
            formats.append({
                'url': url,
                'ext': determine_ext(decoded_url),
                'format_id': str_or_none(play_url.get('vtype')),
                'format_note': str_or_none(play_url.get('definition')),
                'width': int_or_none(play_url.get('vwidth')),
                'height': int_or_none(play_url.get('vheight')),
            })

        return formats

    def _real_extract(self, url):
        uu_mobj = re.search(r'uu=([\w]+)', url)
        vu_mobj = re.search(r'vu=([\w]+)', url)

        if not uu_mobj or not vu_mobj:
            raise ExtractorError('Invalid URL: %s' % url, expected=True)

        uu = uu_mobj.group(1)
        vu = vu_mobj.group(1)
        media_id = uu + '_' + vu

        formats = self._get_formats('flash', uu, vu, media_id) + self._get_formats('html5', uu, vu, media_id)
        self._sort_formats(formats)

        return {
            'id': media_id,
            'title': 'Video %s' % media_id,
            'formats': formats,
        }


code:
import uuid

from .common import InfoExtractor
from ..compat import compat_HTTPError
from ..utils import (
    ExtractorError,
    int_or_none,
    join_nonempty,
    qualities,
)

class LEGOIE(InfoExtractor):
    _VALID_URL = r'https?://(?:www\.)?lego\.com/(?P<locale>[a-z]{2}-[a-z]{2})/(?:[^/]+/)*videos/(?:[^/]+/)*[^/?#]+-(?P<id>[0-9a-f]{32})'
    _TESTS = [{
        'url': 'http://www.lego.com/en-us/videos/themes/club/blocumentary-kawaguchi-55492d823b1b4d5e985787fa8c2973b1',
        'md5': 'f34468f176cfd76488767fc162c405fa',
        'info_dict': {
            'id': '55492d82-3b1b-4d5e-9857-87fa8c2973b1_en-US',
            'ext': 'mp4',
            'title': 'Blocumentary Great Creations: Akiyuki Kawaguchi',
            'description': 'Blocumentary Great Creations: Akiyuki Kawaguchi',
        },
    }, {
        # geo-restricted but the contentUrl contain a valid url
        'url': 'http://www.lego.com/nl-nl/videos/themes/nexoknights/episode-20-kingdom-of-heroes-13bdc2299ab24d9685701a915b3d71e7##sp=399',
        'md5': 'c7420221f7ffd03ff056f9db7f8d807c',
        'info_dict': {
            'id': '13bdc229-9ab2-4d96-8570-1a915b3d71e7_nl-NL',
            'ext': 'mp4',
            'title': 'Aflevering 20:  Helden van het koninkrijk',
            'description': 'md5:8ee499aac26d7fa8bcb0cedb7f9c3941',
            'age_limit': 5,
        },
    }, {
        # with subtitle
        'url': 'https://www.lego.com/nl-nl/kids/videos/classic/creative-storytelling-the-little-puppy-aa24f27c7d5242bc86102ebdc0f24cba',
        'info_dict': {
            'id': 'aa24f27c-7d52-42bc-8610-2ebdc0f24cba_nl-NL',
            'ext': 'mp4',
            'title': 'De kleine puppy',
            'description': 'md5:5b725471f849348ac73f2e12cfb4be06',
            'age_limit': 1,
            'subtitles': {
                'nl': [{
                    'ext': 'srt',
                    'url': r're:^https://.+\.srt$',
                }],
            },
        },
        'params': {
            'skip_download': True,
        },
    }]
    _QUALITIES = {
        'Lowest': (64, 180, 320),
        'Low': (64, 270, 480),
        'Medium': (96, 360, 640),
        'High': (128, 540, 960),
        'Highest': (128, 720, 1280),
    }

    def _real_extract(self, url):
        locale, video_id = self._match_valid_url(url).groups()
        countries = [locale.split('-')[1].upper()]
        self._initialize_geo_bypass({
            'countries': countries,
        })

        try:
            item = self._download_json(
                # https://contentfeed.services.lego.com/api/v2/item/[VIDEO_ID]?culture=[LOCALE]&contentType=Video
                'https://services.slingshot.lego.com/mediaplayer/v2',
                video_id, query={
                    'videoId': '%s_%s' % (uuid.UUID(video_id), locale),
                }, headers=self.geo_verification_headers())
        except ExtractorError as e:
            if isinstance(e.cause, compat_HTTPError) and e.cause.code == 451:
                self.raise_geo_restricted(countries=countries)
            raise

        video = item['Video']
        video_id = video['Id']
        title = video['Title']

        q = qualities(['Lowest', 'Low', 'Medium', 'High', 'Highest'])
        formats = []
        for video_source in item.get('VideoFormats', []):
            video_source_url = video_source.get('Url')
            if not video_source_url:
                continue
            video_source_format = video_source.get('Format')
            if video_source_format == 'F4M':
                formats.extend(self._extract_f4m_formats(
                    video_source_url, video_id,
                    f4m_id=video_source_format, fatal=False))
            elif video_source_format == 'M3U8':
                formats.extend(self._extract_m3u8_formats(
                    video_source_url, video_id, 'mp4', 'm3u8_native',
                    m3u8_id=video_source_format, fatal=False))
            else:
                video_source_quality = video_source.get('Quality')
                f = {
                    'format_id': join_nonempty(video_source_format, video_source_quality),
                    'quality': q(video_source_quality),
                    'url': video_source_url,
                }
                quality = self._QUALITIES.get(video_source_quality)
                if quality:
                    f.update({
                        'abr': quality[0],
                        'height': quality[1],
                        'width': quality[2],
                    }),
                formats.append(f)
        self._sort_formats(formats)

        subtitles = {}
        sub_file_id = video.get('SubFileId')
        if sub_file_id and sub_file_id != '00000000-0000-0000-0000-000000000000':
            net_storage_path = video.get('NetstoragePath')
            invariant_id = video.get('InvariantId')
            video_file_id = video.get('VideoFileId')
            video_version = video.get('VideoVersion')
            if net_storage_path and invariant_id and video_file_id and video_version:
                subtitles.setdefault(locale[:2], []).append({
                    'url': 'https://lc-mediaplayerns-live-s.legocdn.com/public/%s/%s_%s_%s_%s_sub.srt' % (net_storage_path, invariant_id, video_file_id, locale, video_version),
                })

        return {
            'id': video_id,
            'title': title,
            'description': video.get('Description'),
            'thumbnail': video.get('GeneratedCoverImage') or video.get('GeneratedThumbnail'),
            'duration': int_or_none(video.get('Length')),
            'formats': formats,
            'subtitles': subtitles,
            'age_limit': int_or_none(video.get('AgeFrom')),
            'season': video.get('SeasonTitle'),
            'season_number': int_or_none(video.get('Season')) or None,
            'episode_number': int_or_none(video.get('Episode')) or None,
        }

repo_name: Lesmiscore/ytdl


code:
from .common import InfoExtractor

class LemondeIE(InfoExtractor):
    _VALID_URL = r'https?://(?:.+?\.)?lemonde\.fr/(?:[^/]+/)*(?P<id>[^/]+)\.html'
    _TESTS = [{
        'url': 'http://www.lemonde.fr/police-justice/video/2016/01/19/comprendre-l-affaire-bygmalion-en-cinq-minutes_4849702_1653578.html',
        'md5': 'da120c8722d8632eec6ced937536cc98',
        'info_dict': {
            'id': 'lqm3kl',
            'ext': 'mp4',
            'title': "Comprendre l'affaire Bygmalion en 5 minutes",
            'thumbnail': r're:^https?://.*\.jpg',
            'duration': 309,
            'upload_date': '20160119',
            'timestamp': 1453194778,
            'uploader_id': '3pmkp',
        },
    }, {
        # standard iframe embed
        'url': 'http://www.lemonde.fr/les-decodeurs/article/2016/10/18/tout-comprendre-du-ceta-le-petit-cousin-du-traite-transatlantique_5015920_4355770.html',
        'info_dict': {
            'id': 'uzsxms',
            'ext': 'mp4',
            'title': "CETA : quelles suites pour l'accord commercial entre l'Europe et le Canada ?",
            'thumbnail': r're:^https?://.*\.jpg',
            'duration': 325,
            'upload_date': '20161021',
            'timestamp': 1477044540,
            'uploader_id': '3pmkp',
        },
        'params': {
            'skip_download': True,
        },
    }, {
        'url': 'http://redaction.actu.lemonde.fr/societe/video/2016/01/18/calais-debut-des-travaux-de-defrichement-dans-la-jungle_4849233_3224.html',
        'only_matching': True,
    }, {
        # YouTube embeds
        'url': 'http://www.lemonde.fr/pixels/article/2016/12/09/pourquoi-pewdiepie-superstar-de-youtube-a-menace-de-fermer-sa-chaine_5046649_4408996.html',
        'only_matching': True,
    }]

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

        webpage = self._download_webpage(url, display_id)

        digiteka_url = self._proto_relative_url(self._search_regex(
            r'url\s*:\s*(["\'])(?P<url>(?:https?://)?//(?:www\.)?(?:digiteka\.net|ultimedia\.com)/deliver/.+?)\1',
            webpage, 'digiteka url', group='url', default=None))

        if digiteka_url:
            return self.url_result(digiteka_url, 'Digiteka')

        return self.url_result(url, 'Generic')


code:
from .common import InfoExtractor

class LentaIE(InfoExtractor):
    _VALID_URL = r'https?://(?:www\.)?lenta\.ru/[^/]+/\d+/\d+/\d+/(?P<id>[^/?#&]+)'
    _TESTS = [{
        'url': 'https://lenta.ru/news/2018/03/22/savshenko_go/',
        'info_dict': {
            'id': '964400',
            'ext': 'mp4',
            'title': 'Надежду Савченко задержали',
            'thumbnail': r're:^https?://.*\.jpg$',
            'duration': 61,
            'view_count': int,
        },
        'params': {
            'skip_download': True,
        },
    }, {
        # EaglePlatform iframe embed
        'url': 'http://lenta.ru/news/2015/03/06/navalny/',
        'info_dict': {
            'id': '227304',
            'ext': 'mp4',
            'title': 'Навальный вышел на свободу',
            'description': 'md5:d97861ac9ae77377f3f20eaf9d04b4f5',
            'thumbnail': r're:^https?://.*\.jpg$',
            'duration': 87,
            'view_count': int,
            'age_limit': 0,
        },
        'params': {
            'skip_download': 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'vid\s*:\s*["\']?(\d+)', webpage, 'eagleplatform id',
            default=None)
        if video_id:
            return self.url_result(
                'eagleplatform:lentaru.media.eagleplatform.com:%s' % video_id,
                ie='EaglePlatform', video_id=video_id)

        return self.url_result(url, ie='Generic')


code:
import re

from .common import InfoExtractor

from ..utils import (
    determine_ext,
    float_or_none,
    int_or_none,
    parse_filesize,
)

class LibraryOfCongressIE(InfoExtractor):
    IE_NAME = 'loc'
    IE_DESC = 'Library of Congress'
    _VALID_URL = r'https?://(?:www\.)?loc\.gov/(?:item/|today/cyberlc/feature_wdesc\.php\?.*\brec=)(?P<id>[0-9a-z_.]+)'
    _TESTS = [{
        # embedded via <div class="media-player"
        'url': 'http://loc.gov/item/90716351/',
        'md5': '6ec0ae8f07f86731b1b2ff70f046210a',
        'info_dict': {
            'id': '90716351',
            'ext': 'mp4',
            'title': "Pa's trip to Mars",
            'duration': 0,
            'view_count': int,
        },
    }, {
        # webcast embedded via mediaObjectId
        'url': 'https://www.loc.gov/today/cyberlc/feature_wdesc.php?rec=5578',
        'info_dict': {
            'id': '5578',
            'ext': 'mp4',
            'title': 'Help! Preservation Training Needs Here, There & Everywhere',
            'duration': 3765,
            'view_count': int,
            'subtitles': 'mincount:1',
        },
        'params': {
            'skip_download': True,
        },
    }, {
        # with direct download links
        'url': 'https://www.loc.gov/item/78710669/',
        'info_dict': {
            'id': '78710669',
            'ext': 'mp4',
            'title': 'La vie et la passion de Jesus-Christ',
            'duration': 0,
            'view_count': int,
            'formats': 'mincount:4',
        },
        'params': {
            'skip_download': True,
        },
    }, {
        'url': 'https://www.loc.gov/item/ihas.200197114/',
        'only_matching': True,
    }, {
        'url': 'https://www.loc.gov/item/afc1981005_afs20503/',
        'only_matching': True,
    }]

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

        media_id = self._search_regex(
            (r'id=(["\'])media-player-(?P<id>.+?)\1',
             r'<video[^>]+id=(["\'])uuid-(?P<id>.+?)\1',
             r'<video[^>]+data-uuid=(["\'])(?P<id>.+?)\1',
             r'mediaObjectId\s*:\s*(["\'])(?P<id>.+?)\1',
             r'data-tab="share-media-(?P<id>[0-9A-F]{32})"'),
            webpage, 'media id', group='id')

        data = self._download_json(
            'https://media.loc.gov/services/v1/media?id=%s&context=json' % media_id,
            media_id)['mediaObject']

        derivative = data['derivatives'][0]
        media_url = derivative['derivativeUrl']

        title = derivative.get('shortName') or data.get('shortName') or self._og_search_title(
            webpage)

        # Following algorithm was extracted from setAVSource js function
        # found in webpage
        media_url = media_url.replace('rtmp', 'https')

        is_video = data.get('mediaType', 'v').lower() == 'v'
        ext = determine_ext(media_url)
        if ext not in ('mp4', 'mp3'):
            media_url += '.mp4' if is_video else '.mp3'

        formats = []
        if '/vod/mp4:' in media_url:
            formats.append({
                'url': media_url.replace('/vod/mp4:', '/hls-vod/media/') + '.m3u8',
                'format_id': 'hls',
                'ext': 'mp4',
                'protocol': 'm3u8_native',
                'quality': 1,
            })
        http_format = {
            'url': re.sub(r'(://[^/]+/)(?:[^/]+/)*(?:mp4|mp3):', r'\1', media_url),
            'format_id': 'http',
            'quality': 1,
        }
        if not is_video:
            http_format['vcodec'] = 'none'
        formats.append(http_format)

        download_urls = set()
        for m in re.finditer(
                r'<option[^>]+value=(["\'])(?P<url>.+?)\1[^>]+data-file-download=[^>]+>\s*(?P<id>.+?)(?:(?: |\s+)\((?P<size>.+?)\))?\s*<', webpage):
            format_id = m.group('id').lower()
            if format_id in ('gif', 'jpeg'):
                continue
            download_url = m.group('url')
            if download_url in download_urls:
                continue
            download_urls.add(download_url)
            formats.append({
                'url': download_url,
                'format_id': format_id,
                'filesize_approx': parse_filesize(m.group('size')),
            })

        self._sort_formats(formats)

        duration = float_or_none(data.get('duration'))
        view_count = int_or_none(data.get('viewCount'))

        subtitles = {}
        cc_url = data.get('ccUrl')
        if cc_url:
            subtitles.setdefault('en', []).append({
                'url': cc_url,
                'ext': 'ttml',
            })

        return {
            'id': video_id,
            'title': title,
            'thumbnail': self._og_search_thumbnail(webpage, default=None),
            'duration': duration,
            'view_count': view_count,
            'formats': formats,
            'subtitles': subtitles,
        }


code:
from .common import InfoExtractor
from ..utils import (
    clean_html,
    get_element_by_class,
    parse_duration,
    strip_or_none,
    unified_strdate,
)

class LibsynIE(InfoExtractor):
    _VALID_URL = r'(?P<mainurl>https?://html5-player\.libsyn\.com/embed/episode/id/(?P<id>[0-9]+))'
    _EMBED_REGEX = [r']+src=(["\'])(?P<url>(?:https?:)?//html5-player\.libsyn\.com/embed/.+?)\1']

    _TESTS = [{
        'url': 'http://html5-player.libsyn.com/embed/episode/id/6385796/',
        'md5': '2a55e75496c790cdeb058e7e6c087746',
        'info_dict': {
            'id': '6385796',
            'ext': 'mp3',
            'title': "Champion Minded - Developing a Growth Mindset",
            # description fetched using another request:
            # http://html5-player.libsyn.com/embed/getitemdetails?item_id=6385796
            # 'description': 'In this episode, Allistair talks about the importance of developing a growth mindset, not only in sports, but in life too.',
            'upload_date': '20180320',
            'thumbnail': 're:^https?://.*',
        },
    }, {
        'url': 'https://html5-player.libsyn.com/embed/episode/id/3727166/height/75/width/200/theme/standard/direction/no/autoplay/no/autonext/no/thumbnail/no/preload/no/no_addthis/no/',
        'md5': '6c5cb21acd622d754d3b1a92b582ce42',
        'info_dict': {
            'id': '3727166',
            'ext': 'mp3',
            'title': 'Clients From Hell Podcast - How a Sex Toy Company Kickstarted my Freelance Career',
            'upload_date': '20150818',
            'thumbnail': 're:^https?://.*',
        }
    }]

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

        data = self._parse_json(self._search_regex(
            r'var\s+playlistItem\s*=\s*({.+?});',
            webpage, 'JSON data block'), video_id)

        episode_title = data.get('item_title') or get_element_by_class('episode-title', webpage)
        if not episode_title:
            self._search_regex(
                [r'data-title="([^"]+)"', r'<title>(.+?)</title>'],
                webpage, 'episode title')
        episode_title = episode_title.strip()

        podcast_title = strip_or_none(clean_html(self._search_regex(
            r'
([^<]+)
', webpage, 'podcast title',
            default=None) or get_element_by_class('podcast-title', webpage)))

        title = '%s - %s' % (podcast_title, episode_title) if podcast_title else episode_title

        formats = []
        for k, format_id in (('media_url_libsyn', 'libsyn'), ('media_url', 'main'), ('download_link', 'download')):
            f_url = data.get(k)
            if not f_url:
                continue
            formats.append({
                'url': f_url,
                'format_id': format_id,
            })

        description = self._html_search_regex(
            r'
(.+?)
', webpage,
            'description', default=None)
        if description:
            # Strip non-breaking and normal spaces
            description = description.replace('\u00A0', ' ').strip()
        release_date = unified_strdate(self._search_regex(
            r'
Released: ([^<]+)<',
            webpage, 'release date', default=None) or data.get('release_date'))

        return {
            'id': video_id,
            'title': title,
            'description': description,
            'thumbnail': data.get('thumbnail_url'),
            'upload_date': release_date,
            'duration': parse_duration(data.get('duration')),
            'formats': formats,
        }


code:
import re

from .common import InfoExtractor
from ..compat import (
    compat_str,
    compat_urlparse,
)
from ..utils import (
    determine_ext,
    ExtractorError,
    int_or_none,
    parse_iso8601,
    remove_end,
)

class LifeNewsIE(InfoExtractor):
    IE_NAME = 'life'
    IE_DESC = 'Life.ru'
    _VALID_URL = r'https?://life\.ru/t/[^/]+/(?P<id>\d+)'

    _TESTS = [{
        # single video embedded via video/source
        'url': 'https://life.ru/t/новости/98736',
        'md5': '77c95eaefaca216e32a76a343ad89d23',
        'info_dict': {
            'id': '98736',
            'ext': 'mp4',
            'title': 'Мужчина нашел дома архив оборонного завода',
            'description': 'md5:3b06b1b39b5e2bea548e403d99b8bf26',
            'timestamp': 1344154740,
            'upload_date': '20120805',
            'view_count': int,
        }
    }, {
        # single video embedded via iframe
        'url': 'https://life.ru/t/новости/152125',
        'md5': '77d19a6f0886cd76bdbf44b4d971a273',
        'info_dict': {
            'id': '152125',
            'ext': 'mp4',
            'title': 'В Сети появилось видео захвата «Правым сектором» колхозных полей ',
            'description': 'Жители двух поселков Днепропетровской области не простили радикалам угрозу лишения плодородных земель и пошли в лобовую. ',
            'timestamp': 1427961840,
            'upload_date': '20150402',
            'view_count': int,
        }
    }, {
        # two videos embedded via iframe
        'url': 'https://life.ru/t/новости/153461',
        'info_dict': {
            'id': '153461',
            'title': 'В Москве спасли потерявшегося медвежонка, который спрятался на дереве',
            'description': 'Маленький хищник не смог найти дорогу домой и обрел временное убежище на тополе недалеко от жилого массива, пока его не нашла соседская собака.',
            'timestamp': 1430825520,
            'view_count': int,
        },
        'playlist': [{
            'md5': '9b6ef8bc0ffa25aebc8bdb40d89ab795',
            'info_dict': {
                'id': '153461-video1',
                'ext': 'mp4',
                'title': 'В Москве спасли потерявшегося медвежонка, который спрятался на дереве (Видео 1)',
                'description': 'Маленький хищник не смог найти дорогу домой и обрел временное убежище на тополе недалеко от жилого массива, пока его не нашла соседская собака.',
                'timestamp': 1430825520,
                'upload_date': '20150505',
            },
        }, {
            'md5': 'ebb3bf3b1ce40e878d0d628e93eb0322',
            'info_dict': {
                'id': '153461-video2',
                'ext': 'mp4',
                'title': 'В Москве спасли потерявшегося медвежонка, который спрятался на дереве (Видео 2)',
                'description': 'Маленький хищник не смог найти дорогу домой и обрел временное убежище на тополе недалеко от жилого массива, пока его не нашла соседская собака.',
                'timestamp': 1430825520,
                'upload_date': '20150505',
            },
        }],
    }, {
        'url': 'https://life.ru/t/новости/213035',
        'only_matching': True,
    }, {
        'url': 'https://life.ru/t/%D0%BD%D0%BE%D0%B2%D0%BE%D1%81%D1%82%D0%B8/153461',
        'only_matching': True,
    }, {
        'url': 'https://life.ru/t/новости/411489/manuel_vals_nazval_frantsiiu_tsieliu_nomier_odin_dlia_ighil',
        'only_matching': True,
    }]

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

        webpage = self._download_webpage(url, video_id)

        video_urls = re.findall(
            r'<video[^>]+><source[^>]+src=["\'](.+?)["\']', webpage)

        iframe_links = re.findall(
            r'<iframe[^>]+src=["\']((?:https?:)?//embed\.life\.ru/(?:embed|video)/.+?)["\']',
            webpage)

        if not video_urls and not iframe_links:
            raise ExtractorError('No media links available for %s' % video_id)

        title = remove_end(
            self._og_search_title(webpage),
            ' - Life.ru')

        description = self._og_search_description(webpage)

        view_count = self._html_search_regex(
            r'<div[^>]+class=(["\']).*?\bhits-count\b.*?\1[^>]*>\s*(?P<value>\d+)\s*</div>',
            webpage, 'view count', fatal=False, group='value')

        timestamp = parse_iso8601(self._search_regex(
            r'<time[^>]+datetime=(["\'])(?P<value>.+?)\1',
            webpage, 'upload date', fatal=False, group='value'))

        common_info = {
            'description': description,
            'view_count': int_or_none(view_count),
            'timestamp': timestamp,
        }

        def make_entry(video_id, video_url, index=None):
            cur_info = dict(common_info)
            cur_info.update({
                'id': video_id if not index else '%s-video%s' % (video_id, index),
                'url': video_url,
                'title': title if not index else '%s (Видео %s)' % (title, index),
            })
            return cur_info

        def make_video_entry(video_id, video_url, index=None):
            video_url = compat_urlparse.urljoin(url, video_url)
            return make_entry(video_id, video_url, index)

        def make_iframe_entry(video_id, video_url, index=None):
            video_url = self._proto_relative_url(video_url, 'http:')
            cur_info = make_entry(video_id, video_url, index)
            cur_info['_type'] = 'url_transparent'
            return cur_info

        if len(video_urls) == 1 and not iframe_links:
            return make_video_entry(video_id, video_urls[0])

        if len(iframe_links) == 1 and not video_urls:
            return make_iframe_entry(video_id, iframe_links[0])

        entries = []

        if video_urls:
            for num, video_url in enumerate(video_urls, 1):
                entries.append(make_video_entry(video_id, video_url, num))

        if iframe_links:
            for num, iframe_link in enumerate(iframe_links, len(video_urls) + 1):
                entries.append(make_iframe_entry(video_id, iframe_link, num))

        playlist = common_info.copy()
        playlist.update(self.playlist_result(entries, video_id, title, description))
        return playlist

class LifeEmbedIE(InfoExtractor):
    IE_NAME = 'life:embed'
    _VALID_URL = r'https?://embed\.life\.ru/(?:embed|video)/(?P<id>[\da-f]{32})'

    _TESTS = [{
        'url': 'http://embed.life.ru/embed/e50c2dec2867350528e2574c899b8291',
        'md5': 'b889715c9e49cb1981281d0e5458fbbe',
        'info_dict': {
            'id': 'e50c2dec2867350528e2574c899b8291',
            'ext': 'mp4',
            'title': 'e50c2dec2867350528e2574c899b8291',
            'thumbnail': r're:http://.*\.jpg',
        }
    }, {
        # with 1080p
        'url': 'https://embed.life.ru/video/e50c2dec2867350528e2574c899b8291',
        'only_matching': True,
    }]

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

        webpage = self._download_webpage(url, video_id)

        thumbnail = None
        formats = []

        def extract_m3u8(manifest_url):
            formats.extend(self._extract_m3u8_formats(
                manifest_url, video_id, 'mp4',
                entry_protocol='m3u8_native', m3u8_id='m3u8'))

        def extract_original(original_url):
            formats.append({
                'url': original_url,
                'format_id': determine_ext(original_url, None),
                'quality': 1,
            })

        playlist = self._parse_json(
            self._search_regex(
                r'options\s*=\s*({.+?});', webpage, 'options', default='{}'),
            video_id).get('playlist', {})
        if playlist:
            master = playlist.get('master')
            if isinstance(master, compat_str) and determine_ext(master) == 'm3u8':
                extract_m3u8(compat_urlparse.urljoin(url, master))
            original = playlist.get('original')
            if isinstance(original, compat_str):
                extract_original(original)
            thumbnail = playlist.get('image')

        # Old rendition fallback
        if not formats:
            for video_url in re.findall(r'"file"\s*:\s*"([^"]+)', webpage):
                video_url = compat_urlparse.urljoin(url, video_url)
                if determine_ext(video_url) == 'm3u8':
                    extract_m3u8(video_url)
                else:
                    extract_original(video_url)

        self._sort_formats(formats)

        thumbnail = thumbnail or self._search_regex(
            r'"image"\s*:\s*"([^"]+)', webpage, 'thumbnail', default=None)

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


code:
import json

from .common import InfoExtractor
from ..utils import (
    int_or_none,
    js_to_json,
    parse_iso8601,
    str_or_none,
    traverse_obj,
)

class LikeeIE(InfoExtractor):
    IE_NAME = 'likee'
    _VALID_URL = r'(?x)https?://(www\.)?likee\.video/(?:(?P<channel_name>[^/]+)/video/|v/)(?P<id>\w+)'
    _TESTS = [{
        'url': 'https://likee.video/@huynh_hong_quan_/video/7093444807096327263',
        'info_dict': {
            'id': '7093444807096327263',
            'ext': 'mp4',
            'title': '🤴🤴🤴',
            'description': 'md5:9a7ebe816f0e78722ee5ed76f75983b4',
            'thumbnail': r're:^https?://.+\.jpg',
            'uploader': 'Huỳnh Hồng Quân ',
            'play_count': int,
            'download_count': int,
            'artist': 'Huỳnh Hồng Quân ',
            'timestamp': 1651571320,
            'upload_date': '20220503',
            'view_count': int,
            'uploader_id': 'huynh_hong_quan_',
            'duration': 12374,
            'comment_count': int,
            'like_count': int,
        },
    }, {
        'url': 'https://likee.video/@649222262/video/7093167848050058862',
        'info_dict': {
            'id': '7093167848050058862',
            'ext': 'mp4',
            'title': 'likee video #7093167848050058862',
            'description': 'md5:3f971c8c6ee8a216f2b1a9094c5de99f',
            'thumbnail': r're:^https?://.+\.jpg',
            'comment_count': int,
            'like_count': int,
            'uploader': 'Vương Phước Nhi',
            'download_count': int,
            'timestamp': 1651506835,
            'upload_date': '20220502',
            'duration': 60024,
            'play_count': int,
            'artist': 'Vương Phước Nhi',
            'uploader_id': '649222262',
            'view_count': int,
        },
    }, {
        'url': 'https://likee.video/@fernanda_rivasg/video/6932224568407629502',
        'info_dict': {
            'id': '6932224568407629502',
            'ext': 'mp4',
            'title': 'Un trend viejito🔥 #LIKEE #Ferlovers #trend ',
            'description': 'md5:c42b903a72a99d6d8b73e3d1126fbcef',
            'thumbnail': r're:^https?://.+\.jpg',
            'comment_count': int,
            'duration': 9684,
            'uploader_id': 'fernanda_rivasg',
            'view_count': int,
            'play_count': int,
            'artist': 'La Cami La✨',
            'download_count': int,
            'like_count': int,
            'uploader': 'Fernanda Rivas🎶',
            'timestamp': 1614034308,
            'upload_date': '20210222',
        },
    }, {
        'url': 'https://likee.video/v/k6QcOp',
        'info_dict': {
            'id': 'k6QcOp',
            'ext': 'mp4',
            'title': '#AguaChallenge tú ya lo intentaste?😱🤩',
            'description': 'md5:b0cc462689d4ff2b624daa4dba7640d9',
            'thumbnail': r're:^https?://.+\.jpg',
            'comment_count': int,
            'duration': 18014,
            'play_count': int,
            'view_count': int,
            'timestamp': 1611694774,
            'like_count': int,
            'uploader': 'Fernanda Rivas🎶',
            'uploader_id': 'fernanda_rivasg',
            'download_count': int,
            'artist': 'ʟᴇʀɪᴋ_ᴜɴɪᴄᴏʀɴ♡',
            'upload_date': '20210126',
        },
    }, {
        'url': 'https://www.likee.video/@649222262/video/7093167848050058862',
        'only_matching': True,
    }, {
        'url': 'https://www.likee.video/v/k6QcOp',
        'only_matching': True,
    }]

    def _real_extract(self, url):
        video_id = self._match_id(url)
        webpage = self._download_webpage(url, video_id)
        info = self._parse_json(
            self._search_regex(r'window\.data\s=\s({.+?});', webpage, 'video info'),
            video_id, transform_source=js_to_json)
        video_url = traverse_obj(info, 'video_url', ('originVideoInfo', 'video_url'))
        if not video_url:
            self.raise_no_formats('Video was deleted', expected=True)
        formats = [{
            'format_id': 'mp4-with-watermark',
            'url': video_url,
            'height': info.get('video_height'),
            'width': info.get('video_width'),
        }, {
            'format_id': 'mp4-without-watermark',
            'url': video_url.replace('_4', ''),
            'height': info.get('video_height'),
            'width': info.get('video_width'),
            'quality': 1,
        }]
        self._sort_formats(formats)
        return {
            'id': video_id,
            'title': info.get('msgText'),
            'description': info.get('share_desc'),
            'view_count': int_or_none(info.get('video_count')),
            'like_count': int_or_none(info.get('likeCount')),
            'play_count': int_or_none(info.get('play_count')),
            'download_count': int_or_none(info.get('download_count')),
            'comment_count': int_or_none(info.get('comment_count')),
            'uploader': str_or_none(info.get('nick_name')),
            'uploader_id': str_or_none(info.get('likeeId')),
            'artist': str_or_none(traverse_obj(info, ('sound', 'owner_name'))),
            'timestamp': parse_iso8601(info.get('uploadDate')),
            'thumbnail': info.get('coverUrl'),
            'duration': int_or_none(traverse_obj(info, ('option_data', 'dur'))),
            'formats': formats,
        }

class LikeeUserIE(InfoExtractor):
    IE_NAME = 'likee:user'
    _VALID_URL = r'https?://(www\.)?likee\.video/(?P<id>[^/]+)/?$'
    _TESTS = [{
        'url': 'https://likee.video/@fernanda_rivasg',
        'info_dict': {
            'id': '925638334',
            'title': 'fernanda_rivasg',
        },
        'playlist_mincount': 500,
    }, {
        'url': 'https://likee.video/@may_hmoob',
        'info_dict': {
            'id': '2943949041',
            'title': 'may_hmoob',
        },
        'playlist_mincount': 80,
    }]
    _PAGE_SIZE = 50
    _API_GET_USER_VIDEO = 'https://api.like-video.com/likee-activity-flow-micro/videoApi/getUserVideo'

    def _entries(self, user_name, user_id):
        last_post_id = ''
        while True:
            user_videos = self._download_json(
                self._API_GET_USER_VIDEO, user_name,
                data=json.dumps({
                    'uid': user_id,
                    'count': self._PAGE_SIZE,
                    'lastPostId': last_post_id,
                    'tabType': 0,
                }).encode('utf-8'),
                headers={'content-type': 'application/json'},
                note=f'Get user info with lastPostId #{last_post_id}')
            items = traverse_obj(user_videos, ('data', 'videoList'))
            if not items:
                break
            for item in items:
                last_post_id = item['postId']
                yield self.url_result(f'https://likee.video/{user_name}/video/{last_post_id}')

    def _real_extract(self, url):
        user_name = self._match_id(url)
        webpage = self._download_webpage(url, user_name)
        info = self._parse_json(
            self._search_regex(r'window\.data\s*=\s*({.+?});', webpage, 'user info'),
            user_name, transform_source=js_to_json)
        user_id = traverse_obj(info, ('userinfo', 'uid'))
        return self.playlist_result(self._entries(user_name, user_id), user_id, traverse_obj(info, ('userinfo', 'user_name')))


code:
import re

from .common import InfoExtractor
from ..compat import compat_HTTPError
from ..utils import (
    determine_ext,
    float_or_none,
    int_or_none,
    smuggle_url,
    try_get,
    unsmuggle_url,
    ExtractorError,
)

class LimelightBaseIE(InfoExtractor):
    _PLAYLIST_SERVICE_URL = 'http://production-ps.lvp.llnw.net/r/PlaylistService/%s/%s/%s'

    @classmethod
    def _extract_embed_urls(cls, url, webpage):
        lm = {
            'Media': 'media',
            'Channel': 'channel',
            'ChannelList': 'channel_list',
        }

        def smuggle(url):
            return smuggle_url(url, {'source_url': url})

        entries = []
        for kind, video_id in re.findall(
                r'LimelightPlayer\.doLoad(Media|Channel|ChannelList)\(["\'](?P<id>[a-z0-9]{32})',
                webpage):
            entries.append(cls.url_result(
                smuggle('limelight:%s:%s' % (lm[kind], video_id)),
                'Limelight%s' % kind, video_id))
        for mobj in re.finditer(
                # As per [1] class attribute should be exactly equal to
                # LimelightEmbeddedPlayerFlash but numerous examples seen
                # that don't exactly match it (e.g. [2]).
                # 1. http://support.3playmedia.com/hc/en-us/articles/227732408-Limelight-Embedding-the-Captions-Plugin-with-the-Limelight-Player-on-Your-Webpage
                # 2. http://www.sedona.com/FacilitatorTraining2017
                r'''(?sx)
                    <object[^>]+class=(["\'])(?:(?!\1).)*\bLimelightEmbeddedPlayerFlash\b(?:(?!\1).)*\1[^>]*>.*?
                        <param[^>]+
                            name=(["\'])flashVars\2[^>]+
                            value=(["\'])(?:(?!\3).)*(?P<kind>media|channel(?:List)?)Id=(?P<id>[a-z0-9]{32})
                ''', webpage):
            kind, video_id = mobj.group('kind'), mobj.group('id')
            entries.append(cls.url_result(
                smuggle('limelight:%s:%s' % (kind, video_id)),
                'Limelight%s' % kind.capitalize(), video_id))
        # http://support.3playmedia.com/hc/en-us/articles/115009517327-Limelight-Embedding-the-Audio-Description-Plugin-with-the-Limelight-Player-on-Your-Web-Page)
        for video_id in re.findall(
                r'(?s)LimelightPlayerUtil\.embed\s*\(\s*{.*?\bmediaId["\']\s*:\s*["\'](?P<id>[a-z0-9]{32})',
                webpage):
            entries.append(cls.url_result(
                smuggle('limelight:media:%s' % video_id),
                LimelightMediaIE.ie_key(), video_id))
        return entries

    def _call_playlist_service(self, item_id, method, fatal=True, referer=None):
        headers = {}
        if referer:
            headers['Referer'] = referer
        try:
            return self._download_json(
                self._PLAYLIST_SERVICE_URL % (self._PLAYLIST_SERVICE_PATH, item_id, method),
                item_id, 'Downloading PlaylistService %s JSON' % method,
                fatal=fatal, headers=headers)
        except ExtractorError as e:
            if isinstance(e.cause, compat_HTTPError) and e.cause.code == 403:
                error = self._parse_json(e.cause.read().decode(), item_id)['detail']['contentAccessPermission']
                if error == 'CountryDisabled':
                    self.raise_geo_restricted()
                raise ExtractorError(error, expected=True)
            raise

    def _extract(self, item_id, pc_method, mobile_method, referer=None):
        pc = self._call_playlist_service(item_id, pc_method, referer=referer)
        mobile = self._call_playlist_service(
            item_id, mobile_method, fatal=False, referer=referer)
        return pc, mobile

    def _extract_info(self, pc, mobile, i, referer):
        get_item = lambda x, y: try_get(x, lambda x: x[y][i], dict) or {}
        pc_item = get_item(pc, 'playlistItems')
        mobile_item = get_item(mobile, 'mediaList')
        video_id = pc_item.get('mediaId') or mobile_item['mediaId']
        title = pc_item.get('title') or mobile_item['title']

        formats = []
        urls = []
        for stream in pc_item.get('streams', []):
            stream_url = stream.get('url')
            if not stream_url or stream_url in urls:
                continue
            if not self.get_param('allow_unplayable_formats') and stream.get('drmProtected'):
                continue
            urls.append(stream_url)
            ext = determine_ext(stream_url)
            if ext == 'f4m':
                formats.extend(self._extract_f4m_formats(
                    stream_url, video_id, f4m_id='hds', fatal=False))
            else:
                fmt = {
                    'url': stream_url,
                    'abr': float_or_none(stream.get('audioBitRate')),
                    'fps': float_or_none(stream.get('videoFrameRate')),
                    'ext': ext,
                }
                width = int_or_none(stream.get('videoWidthInPixels'))
                height = int_or_none(stream.get('videoHeightInPixels'))
                vbr = float_or_none(stream.get('videoBitRate'))
                if width or height or vbr:
                    fmt.update({
                        'width': width,
                        'height': height,
                        'vbr': vbr,
                    })
                else:
                    fmt['vcodec'] = 'none'
                rtmp = re.search(r'^(?P<url>rtmpe?://(?P<host>[^/]+)/(?P<app>.+))/(?P<playpath>mp[34]:.+)$', stream_url)
                if rtmp:
                    format_id = 'rtmp'
                    if stream.get('videoBitRate'):
                        format_id += '-%d' % int_or_none(stream['videoBitRate'])
                    http_format_id = format_id.replace('rtmp', 'http')

                    CDN_HOSTS = (
                        ('delvenetworks.com', 'cpl.delvenetworks.com'),
                        ('video.llnw.net', 's2.content.video.llnw.net'),
                    )
                    for cdn_host, http_host in CDN_HOSTS:
                        if cdn_host not in rtmp.group('host').lower():
                            continue
                        http_url = 'http://%s/%s' % (http_host, rtmp.group('playpath')[4:])
                        urls.append(http_url)
                        if self._is_valid_url(http_url, video_id, http_format_id):
                            http_fmt = fmt.copy()
                            http_fmt.update({
                                'url': http_url,
                                'format_id': http_format_id,
                            })
                            formats.append(http_fmt)
                            break

                    fmt.update({
                        'url': rtmp.group('url'),
                        'play_path': rtmp.group('playpath'),
                        'app': rtmp.group('app'),
                        'ext': 'flv',
                        'format_id': format_id,
                    })
                formats.append(fmt)

        for mobile_url in mobile_item.get('mobileUrls', []):
            media_url = mobile_url.get('mobileUrl')
            format_id = mobile_url.get('targetMediaPlatform')
            if not media_url or media_url in urls:
                continue
            if (format_id in ('Widevine', 'SmoothStreaming')
                    and not self.get_param('allow_unplayable_formats', False)):
                continue
            urls.append(media_url)
            ext = determine_ext(media_url)
            if ext == 'm3u8':
                formats.extend(self._extract_m3u8_formats(
                    media_url, video_id, 'mp4', 'm3u8_native',
                    m3u8_id=format_id, fatal=False))
            elif ext == 'f4m':
                formats.extend(self._extract_f4m_formats(
                    stream_url, video_id, f4m_id=format_id, fatal=False))
            else:
                formats.append({
                    'url': media_url,
                    'format_id': format_id,
                    'quality': -10,
                    'ext': ext,
                })

        self._sort_formats(formats)

        subtitles = {}
        for flag in mobile_item.get('flags'):
            if flag == 'ClosedCaptions':
                closed_captions = self._call_playlist_service(
                    video_id, 'getClosedCaptionsDetailsByMediaId',
                    False, referer) or []
                for cc in closed_captions:
                    cc_url = cc.get('webvttFileUrl')
                    if not cc_url:
                        continue
                    lang = cc.get('languageCode') or self._search_regex(r'/([a-z]{2})\.vtt', cc_url, 'lang', default='en')
                    subtitles.setdefault(lang, []).append({
                        'url': cc_url,
                    })
                break

        get_meta = lambda x: pc_item.get(x) or mobile_item.get(x)

        return {
            'id': video_id,
            'title': title,
            'description': get_meta('description'),
            'formats': formats,
            'duration': float_or_none(get_meta('durationInMilliseconds'), 1000),
            'thumbnail': get_meta('previewImageUrl') or get_meta('thumbnailImageUrl'),
            'subtitles': subtitles,
        }

class LimelightMediaIE(LimelightBaseIE):
    IE_NAME = 'limelight'
    _VALID_URL = r'''(?x)
                        (?:
                            limelight:media:|
                            https?://
                                (?:
                                    link\.videoplatform\.limelight\.com/media/|
                                    assets\.delvenetworks\.com/player/loader\.swf
                                )
                                \?.*?\bmediaId=
                        )
                        (?P<id>[a-z0-9]{32})
                    '''
    _TESTS = [{
        'url': 'http://link.videoplatform.limelight.com/media/?mediaId=3ffd040b522b4485b6d84effc750cd86',
        'info_dict': {
            'id': '3ffd040b522b4485b6d84effc750cd86',
            'ext': 'mp4',
            'title': 'HaP and the HB Prince Trailer',
            'description': 'md5:8005b944181778e313d95c1237ddb640',
            'thumbnail': r're:^https?://.*\.jpeg$',
            'duration': 144.23,
        },
        'params': {
            # m3u8 download
            'skip_download': True,
        },
    }, {
        # video with subtitles
        'url': 'limelight:media:a3e00274d4564ec4a9b29b9466432335',
        'md5': '2fa3bad9ac321e23860ca23bc2c69e3d',
        'info_dict': {
            'id': 'a3e00274d4564ec4a9b29b9466432335',
            'ext': 'mp4',
            'title': '3Play Media Overview Video',
            'thumbnail': r're:^https?://.*\.jpeg$',
            'duration': 78.101,
            # TODO: extract all languages that were accessible via API
            # 'subtitles': 'mincount:9',
            'subtitles': 'mincount:1',
        },
    }, {
        'url': 'https://assets.delvenetworks.com/player/loader.swf?mediaId=8018a574f08d416e95ceaccae4ba0452',
        'only_matching': True,
    }]
    _PLAYLIST_SERVICE_PATH = 'media'

    def _real_extract(self, url):
        url, smuggled_data = unsmuggle_url(url, {})
        video_id = self._match_id(url)
        source_url = smuggled_data.get('source_url')
        self._initialize_geo_bypass({
            'countries': smuggled_data.get('geo_countries'),
        })

        pc, mobile = self._extract(
            video_id, 'getPlaylistByMediaId',
            'getMobilePlaylistByMediaId', source_url)

        return self._extract_info(pc, mobile, 0, source_url)

class LimelightChannelIE(LimelightBaseIE):
    IE_NAME = 'limelight:channel'
    _VALID_URL = r'''(?x)
                        (?:
                            limelight:channel:|
                            https?://
                                (?:
                                    link\.videoplatform\.limelight\.com/media/|
                                    assets\.delvenetworks\.com/player/loader\.swf
                                )
                                \?.*?\bchannelId=
                        )
                        (?P<id>[a-z0-9]{32})
                    '''
    _TESTS = [{
        'url': 'http://link.videoplatform.limelight.com/media/?channelId=ab6a524c379342f9b23642917020c082',
        'info_dict': {
            'id': 'ab6a524c379342f9b23642917020c082',
            'title': 'Javascript Sample Code',
            'description': 'Javascript Sample Code - http://www.delvenetworks.com/sample-code/playerCode-demo.html',
        },
        'playlist_mincount': 3,
    }, {
        'url': 'http://assets.delvenetworks.com/player/loader.swf?channelId=ab6a524c379342f9b23642917020c082',
        'only_matching': True,
    }]
    _PLAYLIST_SERVICE_PATH = 'channel'

    def _real_extract(self, url):
        url, smuggled_data = unsmuggle_url(url, {})
        channel_id = self._match_id(url)
        source_url = smuggled_data.get('source_url')

        pc, mobile = self._extract(
            channel_id, 'getPlaylistByChannelId',
            'getMobilePlaylistWithNItemsByChannelId?begin=0&count=-1',
            source_url)

        entries = [
            self._extract_info(pc, mobile, i, source_url)
            for i in range(len(pc['playlistItems']))]

        return self.playlist_result(
            entries, channel_id, pc.get('title'), mobile.get('description'))

class LimelightChannelListIE(LimelightBaseIE):
    IE_NAME = 'limelight:channel_list'
    _VALID_URL = r'''(?x)
                        (?:
                            limelight:channel_list:|
                            https?://
                                (?:
                                    link\.videoplatform\.limelight\.com/media/|
                                    assets\.delvenetworks\.com/player/loader\.swf
                                )
                                \?.*?\bchannelListId=
                        )
                        (?P<id>[a-z0-9]{32})
                    '''
    _TESTS = [{
        'url': 'http://link.videoplatform.limelight.com/media/?channelListId=301b117890c4465c8179ede21fd92e2b',
        'info_dict': {
            'id': '301b117890c4465c8179ede21fd92e2b',
            'title': 'Website - Hero Player',
        },
        'playlist_mincount': 2,
    }, {
        'url': 'https://assets.delvenetworks.com/player/loader.swf?channelListId=301b117890c4465c8179ede21fd92e2b',
        'only_matching': True,
    }]
    _PLAYLIST_SERVICE_PATH = 'channel_list'

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

        channel_list = self._call_playlist_service(
            channel_list_id, 'getMobileChannelListById')

        entries = [
            self.url_result('limelight:channel:%s' % channel['id'], 'LimelightChannel')
            for channel in channel_list['channelList']]

        return self.playlist_result(
            entries, channel_list_id, channel_list['title'])


code:
from .common import InfoExtractor
from ..compat import compat_str
from ..utils import (
    format_field,
    int_or_none,
    str_or_none,
)

class LineLiveBaseIE(InfoExtractor):
    _API_BASE_URL = 'https://live-api.line-apps.com/web/v4.0/channel/'

    def _parse_broadcast_item(self, item):
        broadcast_id = compat_str(item['id'])
        title = item['title']
        is_live = item.get('isBroadcastingNow')

        thumbnails = []
        for thumbnail_id, thumbnail_url in (item.get('thumbnailURLs') or {}).items():
            if not thumbnail_url:
                continue
            thumbnails.append({
                'id': thumbnail_id,
                'url': thumbnail_url,
            })

        channel = item.get('channel') or {}
        channel_id = str_or_none(channel.get('id'))

        return {
            'id': broadcast_id,
            'title': title,
            'thumbnails': thumbnails,
            'timestamp': int_or_none(item.get('createdAt')),
            'channel': channel.get('name'),
            'channel_id': channel_id,
            'channel_url': format_field(channel_id, None, 'https://live.line.me/channels/%s'),
            'duration': int_or_none(item.get('archiveDuration')),
            'view_count': int_or_none(item.get('viewerCount')),
            'comment_count': int_or_none(item.get('chatCount')),
            'is_live': is_live,
        }

class LineLiveIE(LineLiveBaseIE):
    _VALID_URL = r'https?://live\.line\.me/channels/(?P<channel_id>\d+)/broadcast/(?P<id>\d+)'
    _TESTS = [{
        'url': 'https://live.line.me/channels/5833718/broadcast/18373277',
        'md5': '2c15843b8cb3acd55009ddcb2db91f7c',
        'info_dict': {
            'id': '18373277',
            'title': '2021/12/05 （15分犬）定例譲渡会🐶',
            'ext': 'mp4',
            'timestamp': 1638674925,
            'upload_date': '20211205',
            'thumbnail': 'md5:e1f5817e60f4a72b7e43377cf308d7ef',
            'channel_url': 'https://live.line.me/channels/5833718',
            'channel': 'Yahooニュース掲載🗞プロフ見てね🐕🐕',
            'channel_id': '5833718',
            'duration': 937,
            'view_count': int,
            'comment_count': int,
            'is_live': False,
        }
    }, {
        # archiveStatus == 'DELETED'
        'url': 'https://live.line.me/channels/4778159/broadcast/16378488',
        'only_matching': True,
    }]

    def _real_extract(self, url):
        channel_id, broadcast_id = self._match_valid_url(url).groups()
        broadcast = self._download_json(
            self._API_BASE_URL + '%s/broadcast/%s' % (channel_id, broadcast_id),
            broadcast_id)
        item = broadcast['item']
        info = self._parse_broadcast_item(item)
        protocol = 'm3u8' if info['is_live'] else 'm3u8_native'
        formats = []
        for k, v in (broadcast.get(('live' if info['is_live'] else 'archived') + 'HLSURLs') or {}).items():
            if not v:
                continue
            if k == 'abr':
                formats.extend(self._extract_m3u8_formats(
                    v, broadcast_id, 'mp4', protocol,
                    m3u8_id='hls', fatal=False))
                continue
            f = {
                'ext': 'mp4',
                'format_id': 'hls-' + k,
                'protocol': protocol,
                'url': v,
            }
            if not k.isdigit():
                f['vcodec'] = 'none'
            formats.append(f)
        if not formats:
            archive_status = item.get('archiveStatus')
            if archive_status != 'ARCHIVED':
                self.raise_no_formats('this video has been ' + archive_status.lower(), expected=True)
        self._sort_formats(formats)
        info['formats'] = formats
        return info

class LineLiveChannelIE(LineLiveBaseIE):
    _VALID_URL = r'https?://live\.line\.me/channels/(?P<id>\d+)(?!/broadcast/\d+)(?:[/?&#]|$)'
    _TEST = {
        'url': 'https://live.line.me/channels/5893542',
        'info_dict': {
            'id': '5893542',
            'title': 'いくらちゃんだよぉ🦒',
            'description': 'md5:4d418087973ad081ceb1b3481f0b1816',
        },
        'playlist_mincount': 29
    }

    def _archived_broadcasts_entries(self, archived_broadcasts, channel_id):
        while True:
            for row in (archived_broadcasts.get('rows') or []):
                share_url = str_or_none(row.get('shareURL'))
                if not share_url:
                    continue
                info = self._parse_broadcast_item(row)
                info.update({
                    '_type': 'url',
                    'url': share_url,
                    'ie_key': LineLiveIE.ie_key(),
                })
                yield info
            if not archived_broadcasts.get('hasNextPage'):
                return
            archived_broadcasts = self._download_json(
                self._API_BASE_URL + channel_id + '/archived_broadcasts',
                channel_id, query={
                    'lastId': info['id'],
                })

    def _real_extract(self, url):
        channel_id = self._match_id(url)
        channel = self._download_json(self._API_BASE_URL + channel_id, channel_id)
        return self.playlist_result(
            self._archived_broadcasts_entries(channel.get('archivedBroadcasts') or {}, channel_id),
            channel_id, channel.get('title'), channel.get('information'))


code:
from itertools import zip_longest
import re

from .common import InfoExtractor
from ..utils import (
    clean_html,
    extract_attributes,
    ExtractorError,
    float_or_none,
    get_element_by_class,
    int_or_none,
    srt_subtitles_timecode,
    strip_or_none,
    mimetype2ext,
    try_get,
    urlencode_postdata,
    urljoin,
)

class LinkedInBaseIE(InfoExtractor):
    _NETRC_MACHINE = 'linkedin'
    _logged_in = False

    def _perform_login(self, username, password):
        if self._logged_in:
            return

        login_page = self._download_webpage(
            self._LOGIN_URL, None, 'Downloading login page')
        action_url = urljoin(self._LOGIN_URL, self._search_regex(
            r'<form[^>]+action=(["\'])(?P<url>.+?)\1', login_page, 'post url',
            default='https://www.linkedin.com/uas/login-submit', group='url'))
        data = self._hidden_inputs(login_page)
        data.update({
            'session_key': username,
            'session_password': password,
        })
        login_submit_page = self._download_webpage(
            action_url, None, 'Logging in',
            data=urlencode_postdata(data))
        error = self._search_regex(
            r'<span[^>]+class="error"[^>]*>\s*(.+?)\s*</span>',
            login_submit_page, 'error', default=None)
        if error:
            raise ExtractorError(error, expected=True)
        LinkedInBaseIE._logged_in = True

class LinkedInLearningBaseIE(LinkedInBaseIE):
    _LOGIN_URL = 'https://www.linkedin.com/uas/login?trk=learning'

    def _call_api(self, course_slug, fields, video_slug=None, resolution=None):
        query = {
            'courseSlug': course_slug,
            'fields': fields,
            'q': 'slugs',
        }
        sub = ''
        if video_slug:
            query.update({
                'videoSlug': video_slug,
                'resolution': '_%s' % resolution,
            })
            sub = ' %dp' % resolution
        api_url = 'https://www.linkedin.com/learning-api/detailedCourses'
        if not self._get_cookies(api_url).get('JSESSIONID'):
            self.raise_login_required()
        return self._download_json(
            api_url, video_slug, 'Downloading%s JSON metadata' % sub, headers={
                'Csrf-Token': self._get_cookies(api_url)['JSESSIONID'].value,
            }, query=query)['elements'][0]

    def _get_urn_id(self, video_data):
        urn = video_data.get('urn')
        if urn:
            mobj = re.search(r'urn:li:lyndaCourse:\d+,(\d+)', urn)
            if mobj:
                return mobj.group(1)

    def _get_video_id(self, video_data, course_slug, video_slug):
        return self._get_urn_id(video_data) or '%s/%s' % (course_slug, video_slug)

class LinkedInIE(LinkedInBaseIE):
    _VALID_URL = r'https?://(?:www\.)?linkedin\.com/posts/.+?(?P<id>\d+)'
    _TESTS = [{
        'url': 'https://www.linkedin.com/posts/mishalkhawaja_sendinblueviews-toronto-digitalmarketing-ugcPost-6850898786781339649-mM20',
        'info_dict': {
            'id': '6850898786781339649',
            'ext': 'mp4',
            'title': 'Mishal K. on LinkedIn: #sendinblueviews #toronto #digitalmarketing',
            'description': 'md5:be125430bab1c574f16aeb186a4d5b19',
            'creator': 'Mishal K.'
        },
    }]

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

        title = self._html_extract_title(webpage)
        description = clean_html(get_element_by_class('share-update-card__update-text', webpage))
        like_count = int_or_none(get_element_by_class('social-counts-reactions__social-counts-numRections', webpage))
        creator = strip_or_none(clean_html(get_element_by_class('comment__actor-name', webpage)))

        sources = self._parse_json(extract_attributes(self._search_regex(r'(<video[^>]+>)', webpage, 'video'))['data-sources'], video_id)
        formats = [{
            'url': source['src'],
            'ext': mimetype2ext(source.get('type')),
            'tbr': float_or_none(source.get('data-bitrate'), scale=1000),
        } for source in sources]

        self._sort_formats(formats)

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

class LinkedInLearningIE(LinkedInLearningBaseIE):
    IE_NAME = 'linkedin:learning'
    _VALID_URL = r'https?://(?:www\.)?linkedin\.com/learning/(?P<course_slug>[^/]+)/(?P<id>[^/?#]+)'
    _TEST = {
        'url': 'https://www.linkedin.com/learning/programming-foundations-fundamentals/welcome?autoplay=true',
        'md5': 'a1d74422ff0d5e66a792deb996693167',
        'info_dict': {
            'id': '90426',
            'ext': 'mp4',
            'title': 'Welcome',
            'timestamp': 1430396150.82,
            'upload_date': '20150430',
        },
    }

    def json2srt(self, transcript_lines, duration=None):
        srt_data = ''
        for line, (line_dict, next_dict) in enumerate(zip_longest(transcript_lines, transcript_lines[1:])):
            start_time, caption = line_dict['transcriptStartAt'] / 1000, line_dict['caption']
            end_time = next_dict['transcriptStartAt'] / 1000 if next_dict else duration or start_time + 1
            srt_data += '%d\n%s --> %s\n%s\n\n' % (line + 1, srt_subtitles_timecode(start_time),
                                                   srt_subtitles_timecode(end_time),
                                                   caption)
        return srt_data

    def _real_extract(self, url):
        course_slug, video_slug = self._match_valid_url(url).groups()

        formats = []
        for width, height in ((640, 360), (960, 540), (1280, 720)):
            video_data = self._call_api(
                course_slug, 'selectedVideo', video_slug, height)['selectedVideo']

            video_url_data = video_data.get('url') or {}
            progressive_url = video_url_data.get('progressiveUrl')
            if progressive_url:
                formats.append({
                    'format_id': 'progressive-%dp' % height,
                    'url': progressive_url,
                    'ext': 'mp4',
                    'height': height,
                    'width': width,
                    'source_preference': 1,
                })

        title = video_data['title']

        audio_url = video_data.get('audio', {}).get('progressiveUrl')
        if audio_url:
            formats.append({
                'abr': 64,
                'ext': 'm4a',
                'format_id': 'audio',
                'url': audio_url,
                'vcodec': 'none',
            })

        streaming_url = video_url_data.get('streamingUrl')
        if streaming_url:
            formats.extend(self._extract_m3u8_formats(
                streaming_url, video_slug, 'mp4',
                'm3u8_native', m3u8_id='hls', fatal=False))

        # It seems like this would be correctly handled by default
        # However, unless someone can confirm this, the old
        # behaviour is being kept as-is
        self._sort_formats(formats, ('res', 'source_preference'))
        subtitles = {}
        duration = int_or_none(video_data.get('durationInSeconds'))
        transcript_lines = try_get(video_data, lambda x: x['transcript']['lines'], expected_type=list)
        if transcript_lines:
            subtitles['en'] = [{
                'ext': 'srt',
                'data': self.json2srt(transcript_lines, duration)
            }]

        return {
            'id': self._get_video_id(video_data, course_slug, video_slug),
            'title': title,
            'formats': formats,
            'thumbnail': video_data.get('defaultThumbnail'),
            'timestamp': float_or_none(video_data.get('publishedOn'), 1000),
            'duration': duration,
            'subtitles': subtitles,
        }

class LinkedInLearningCourseIE(LinkedInLearningBaseIE):
    IE_NAME = 'linkedin:learning:course'
    _VALID_URL = r'https?://(?:www\.)?linkedin\.com/learning/(?P<id>[^/?#]+)'
    _TEST = {
        'url': 'https://www.linkedin.com/learning/programming-foundations-fundamentals',
        'info_dict': {
            'id': 'programming-foundations-fundamentals',
            'title': 'Programming Foundations: Fundamentals',
            'description': 'md5:76e580b017694eb89dc8e8923fff5c86',
        },
        'playlist_mincount': 61,
    }

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

    def _real_extract(self, url):
        course_slug = self._match_id(url)
        course_data = self._call_api(course_slug, 'chapters,description,title')

        entries = []
        for chapter_number, chapter in enumerate(course_data.get('chapters', []), 1):
            chapter_title = chapter.get('title')
            chapter_id = self._get_urn_id(chapter)
            for video in chapter.get('videos', []):
                video_slug = video.get('slug')
                if not video_slug:
                    continue
                entries.append({
                    '_type': 'url_transparent',
                    'id': self._get_video_id(video, course_slug, video_slug),
                    'title': video.get('title'),
                    'url': 'https://www.linkedin.com/learning/%s/%s' % (course_slug, video_slug),
                    'chapter': chapter_title,
                    'chapter_number': chapter_number,
                    'chapter_id': chapter_id,
                    'ie_key': LinkedInLearningIE.ie_key(),
                })

        return self.playlist_result(
            entries, course_slug,
            course_data.get('title'),
            course_data.get('description'))


code:
import json
import random

from .common import InfoExtractor
from ..compat import (
    compat_b64decode,
    compat_HTTPError,
    compat_str,
)
from ..utils import (
    clean_html,
    ExtractorError,
    js_to_json,
    parse_duration,
    try_get,
    unified_timestamp,
    urlencode_postdata,
    urljoin,
)

class LinuxAcademyIE(InfoExtractor):
    _VALID_URL = r'''(?x)
                    https?://
                        (?:www\.)?linuxacademy\.com/cp/
                        (?:
                            courses/lesson/course/(?P<chapter_id>\d+)/lesson/(?P<lesson_id>\d+)|
                            modules/view/id/(?P<course_id>\d+)
                        )
                    '''
    _TESTS = [{
        'url': 'https://linuxacademy.com/cp/courses/lesson/course/7971/lesson/2/module/675',
        'info_dict': {
            'id': '7971-2',
            'ext': 'mp4',
            'title': 'What Is Data Science',
            'description': 'md5:c574a3c20607144fb36cb65bdde76c99',
            'timestamp': int,  # The timestamp and upload date changes
            'upload_date': r're:\d+',
            'duration': 304,
        },
        'params': {
            'skip_download': True,
        },
        'skip': 'Requires Linux Academy account credentials',
    }, {
        'url': 'https://linuxacademy.com/cp/courses/lesson/course/1498/lesson/2',
        'only_matching': True,
    }, {
        'url': 'https://linuxacademy.com/cp/modules/view/id/154',
        'info_dict': {
            'id': '154',
            'title': 'AWS Certified Cloud Practitioner',
            'description': 'md5:a68a299ca9bb98d41cca5abc4d4ce22c',
            'duration': 28835,
        },
        'playlist_count': 41,
        'skip': 'Requires Linux Academy account credentials',
    }, {
        'url': 'https://linuxacademy.com/cp/modules/view/id/39',
        'info_dict': {
            'id': '39',
            'title': 'Red Hat Certified Systems Administrator - RHCSA (EX200) Exam Prep  (legacy)',
            'description': 'md5:0f1d3369e90c3fb14a79813b863c902f',
            'duration': 89280,
        },
        'playlist_count': 73,
        'skip': 'Requires Linux Academy account credentials',
    }]

    _AUTHORIZE_URL = 'https://login.linuxacademy.com/authorize'
    _ORIGIN_URL = 'https://linuxacademy.com'
    _CLIENT_ID = 'KaWxNn1C2Gc7n83W9OFeXltd8Utb5vvx'
    _NETRC_MACHINE = 'linuxacademy'

    def _perform_login(self, username, password):
        def random_string():
            return ''.join([
                random.choice('0123456789ABCDEFGHIJKLMNOPQRSTUVXYZabcdefghijklmnopqrstuvwxyz-._~')
                for _ in range(32)])

        webpage, urlh = self._download_webpage_handle(
            self._AUTHORIZE_URL, None, 'Downloading authorize page', query={
                'client_id': self._CLIENT_ID,
                'response_type': 'token id_token',
                'response_mode': 'web_message',
                'redirect_uri': self._ORIGIN_URL,
                'scope': 'openid email user_impersonation profile',
                'audience': self._ORIGIN_URL,
                'state': random_string(),
                'nonce': random_string(),
            })

        login_data = self._parse_json(
            self._search_regex(
                r'atob\(\s*(["\'])(?P<value>(?:(?!\1).)+)\1', webpage,
                'login info', group='value'), None,
            transform_source=lambda x: compat_b64decode(x).decode('utf-8')
        )['extraParams']

        login_data.update({
            'client_id': self._CLIENT_ID,
            'redirect_uri': self._ORIGIN_URL,
            'tenant': 'lacausers',
            'connection': 'Username-Password-ACG-Proxy',
            'username': username,
            'password': password,
            'sso': 'true',
        })

        login_state_url = urlh.geturl()

        try:
            login_page = self._download_webpage(
                'https://login.linuxacademy.com/usernamepassword/login', None,
                'Downloading login page', data=json.dumps(login_data).encode(),
                headers={
                    'Content-Type': 'application/json',
                    'Origin': 'https://login.linuxacademy.com',
                    'Referer': login_state_url,
                })
        except ExtractorError as e:
            if isinstance(e.cause, compat_HTTPError) and e.cause.code == 401:
                error = self._parse_json(e.cause.read(), None)
                message = error.get('description') or error['code']
                raise ExtractorError(
                    '%s said: %s' % (self.IE_NAME, message), expected=True)
            raise

        callback_page, urlh = self._download_webpage_handle(
            'https://login.linuxacademy.com/login/callback', None,
            'Downloading callback page',
            data=urlencode_postdata(self._hidden_inputs(login_page)),
            headers={
                'Content-Type': 'application/x-www-form-urlencoded',
                'Origin': 'https://login.linuxacademy.com',
                'Referer': login_state_url,
            })

        access_token = self._search_regex(
            r'access_token=([^=&]+)', urlh.geturl(),
            'access token', default=None)
        if not access_token:
            access_token = self._parse_json(
                self._search_regex(
                    r'authorizationResponse\s*=\s*({.+?})\s*;', callback_page,
                    'authorization response'), None,
                transform_source=js_to_json)['response']['access_token']

        self._download_webpage(
            'https://linuxacademy.com/cp/login/tokenValidateLogin/token/%s'
            % access_token, None, 'Downloading token validation page')

    def _real_extract(self, url):
        mobj = self._match_valid_url(url)
        chapter_id, lecture_id, course_id = mobj.group('chapter_id', 'lesson_id', 'course_id')
        item_id = course_id if course_id else '%s-%s' % (chapter_id, lecture_id)

        webpage = self._download_webpage(url, item_id)

        # course path
        if course_id:
            module = self._parse_json(
                self._search_regex(
                    r'window\.module\s*=\s*({(?:(?!};)[^"]|"([^"]|\\")*")+})\s*;', webpage, 'module'),
                item_id)
            entries = []
            chapter_number = None
            chapter = None
            chapter_id = None
            for item in module['items']:
                if not isinstance(item, dict):
                    continue

                def type_field(key):
                    return (try_get(item, lambda x: x['type'][key], compat_str) or '').lower()
                type_fields = (type_field('name'), type_field('slug'))
                # Move to next module section
                if 'section' in type_fields:
                    chapter = item.get('course_name')
                    chapter_id = item.get('course_module')
                    chapter_number = 1 if not chapter_number else chapter_number + 1
                    continue
                # Skip non-lessons
                if 'lesson' not in type_fields:
                    continue
                lesson_url = urljoin(url, item.get('url'))
                if not lesson_url:
                    continue
                title = item.get('title') or item.get('lesson_name')
                description = item.get('md_desc') or clean_html(item.get('description')) or clean_html(item.get('text'))
                entries.append({
                    '_type': 'url_transparent',
                    'url': lesson_url,
                    'ie_key': LinuxAcademyIE.ie_key(),
                    'title': title,
                    'description': description,
                    'timestamp': unified_timestamp(item.get('date')) or unified_timestamp(item.get('created_on')),
                    'duration': parse_duration(item.get('duration')),
                    'chapter': chapter,
                    'chapter_id': chapter_id,
                    'chapter_number': chapter_number,
                })
            return {
                '_type': 'playlist',
                'entries': entries,
                'id': course_id,
                'title': module.get('title'),
                'description': module.get('md_desc') or clean_html(module.get('desc')),
                'duration': parse_duration(module.get('duration')),
            }

        # single video path
        m3u8_url = self._parse_json(
            self._search_regex(
                r'player\.playlist\s*=\s*(\[.+?\])\s*;', webpage, 'playlist'),
            item_id)[0]['file']
        formats = self._extract_m3u8_formats(
            m3u8_url, item_id, 'mp4', entry_protocol='m3u8_native',
            m3u8_id='hls')
        self._sort_formats(formats)
        info = {
            'id': item_id,
            'formats': formats,
        }
        lesson = self._parse_json(
            self._search_regex(
                (r'window\.lesson\s*=\s*({.+?})\s*;',
                 r'player\.lesson\s*=\s*({.+?})\s*;'),
                webpage, 'lesson', default='{}'), item_id, fatal=False)
        if lesson:
            info.update({
                'title': lesson.get('lesson_name'),
                'description': lesson.get('md_desc') or clean_html(lesson.get('desc')),
                'timestamp': unified_timestamp(lesson.get('date')) or unified_timestamp(lesson.get('created_on')),
                'duration': parse_duration(lesson.get('duration')),
            })
        if not info.get('title'):
            info['title'] = self._search_regex(
                (r'>Lecture\s*:\s*(?P<value>[^<]+)',
                 r'lessonName\s*=\s*(["\'])(?P<value>(?:(?!\1).)+)\1'), webpage,
                'title', group='value')
        return info


code:
from .common import InfoExtractor
from .vidio import VidioIE

class Liputan6IE(InfoExtractor):
    _VALID_URL = r'https?://www\.liputan6\.com/\w+/read/\d+/(?P<id>[\w-]+)'
    _TESTS = [{
        'url': 'https://www.liputan6.com/news/read/5007510/video-duh-perawat-rs-di-medan-diduga-salah-berikan-obat-ke-pasien',
        'info_dict': {
            'id': '7082548',
            'ext': 'mp4',
            'title': 'Duh, Perawat RS di Medan Diduga Salah Berikan Obat Ke Pasien',
            'thumbnail': 'https://thumbor.prod.vidiocdn.com/lOz5pStm9X-jjlTa_VQQUelOPtw=/640x360/filters:quality(70)/vidio-web-prod-video/uploads/video/image/7082548/duh-perawat-rs-di-medan-diduga-salah-berikan-obat-ke-pasien-ca1125.jpg',
            'channel_id': '185693',
            'uploader': 'Liputan6.com',
            'duration': 104,
            'uploader_url': 'https://www.vidio.com/@liputan6',
            'description': 'md5:3b58ecff10ec3a41d4304cf98228435a',
            'timestamp': 1657159427,
            'uploader_id': 'liputan6',
            'display_id': 'video-duh-perawat-rs-di-medan-diduga-salah-berikan-obat-ke-pasien',
            'like_count': int,
            'view_count': int,
            'comment_count': int,
            'tags': ['perawat indonesia', 'rumah sakit', 'Medan', 'viral hari ini', 'viral', 'enamplus'],
            'channel': 'Default Channel',
            'dislike_count': int,
            'upload_date': '20220707'
        }
    }, {
        'url': 'https://www.liputan6.com/tv/read/5007719/video-program-minyakita-minyak-goreng-kemasan-sederhana-seharga-rp-14-ribu',
        'info_dict': {
            'id': '7082543',
            'ext': 'mp4',
            'title': 'md5:ecb7b3c598b97798bfd0eb50c6233b8c',
            'channel_id': '604054',
            'dislike_count': int,
            'comment_count': int,
            'timestamp': 1657159211,
            'upload_date': '20220707',
            'tags': ['minyakita', 'minyak goreng', 'liputan 6', 'sctv'],
            'uploader_url': 'https://www.vidio.com/@sctv',
            'display_id': 'video-program-minyakita-minyak-goreng-kemasan-sederhana-seharga-rp-14-ribu',
            'like_count': int,
            'uploader': 'SCTV',
            'description': 'md5:6c374d82589b71fb98b3d550edb6873f',
            'duration': 99,
            'uploader_id': 'sctv',
            'thumbnail': 'https://thumbor.prod.vidiocdn.com/AAIOjz-64hKojjdw5hr0oNNEeJg=/640x360/filters:quality(70)/vidio-web-prod-video/uploads/video/image/7082543/program-minyakita-minyak-goreng-kemasan-sederhana-seharga-rp14-ribu-_-liputan-6-7d9fbb.jpg',
            'channel': 'Liputan 6 Pagi',
            'view_count': int,
        }
    }]

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

        json_data = self._search_json(
            r'window.kmklabs.gtm\s*=\s*', webpage, 'json_data', display_id)
        video_id = json_data['videos']['video_1']['video_id']

        return self.url_result(
            f'https://www.vidio.com/watch/{video_id}-{display_id}', ie=VidioIE, video_id=display_id)


code:
import json

from .common import InfoExtractor
from ..utils import (
    ExtractorError,
    int_or_none,
    traverse_obj,
    smuggle_url,
    unsmuggle_url,
)

class LiTVIE(InfoExtractor):
    _VALID_URL = r'https?://(?:www\.)?litv\.tv/(?:vod|promo)/[^/]+/(?:content\.do)?\?.*?\b(?:content_)?id=(?P<id>[^&]+)'

    _URL_TEMPLATE = 'https://www.litv.tv/vod/%s/content.do?id=%s'

    _TESTS = [{
        'url': 'https://www.litv.tv/vod/drama/content.do?brc_id=root&id=VOD00041610&isUHEnabled=true&autoPlay=1',
        'info_dict': {
            'id': 'VOD00041606',
            'title': '花千骨',
        },
        'playlist_count': 50,
    }, {
        'url': 'https://www.litv.tv/vod/drama/content.do?brc_id=root&id=VOD00041610&isUHEnabled=true&autoPlay=1',
        'md5': '969e343d9244778cb29acec608e53640',
        'info_dict': {
            'id': 'VOD00041610',
            'ext': 'mp4',
            'title': '花千骨第1集',
            'thumbnail': r're:https?://.*\.jpg$',
            'description': 'md5:c7017aa144c87467c4fb2909c4b05d6f',
            'episode_number': 1,
        },
        'params': {
            'noplaylist': True,
        },
        'skip': 'Georestricted to Taiwan',
    }, {
        'url': 'https://www.litv.tv/promo/miyuezhuan/?content_id=VOD00044841&',
        'md5': '88322ea132f848d6e3e18b32a832b918',
        'info_dict': {
            'id': 'VOD00044841',
            'ext': 'mp4',
            'title': '芈月傳第1集　霸星芈月降世楚國',
            'description': '楚威王二年，太史令唐昧夜觀星象，發現霸星即將現世。王后得知霸星的預言後，想盡辦法不讓孩子順利出生，幸得莒姬相護化解危機。沒想到眾人期待下出生的霸星卻是位公主，楚威王對此失望至極。楚王后命人將女嬰丟棄河中，居然奇蹟似的被少司命像攔下，楚威王認為此女非同凡響，為她取名芈月。',
        },
        'skip': 'Georestricted to Taiwan',
    }]

    def _extract_playlist(self, season_list, video_id, program_info, prompt=True):
        episode_title = program_info['title']
        content_id = season_list['contentId']

        all_episodes = [
            self.url_result(smuggle_url(
                self._URL_TEMPLATE % (program_info['contentType'], episode['contentId']),
                {'force_noplaylist': True}))  # To prevent infinite recursion
            for episode in season_list['episode']]

        return self.playlist_result(all_episodes, content_id, episode_title)

    def _real_extract(self, url):
        url, smuggled_data = unsmuggle_url(url, {})

        video_id = self._match_id(url)

        webpage = self._download_webpage(url, video_id)

        program_info = self._parse_json(self._search_regex(
            r'var\s+programInfo\s*=\s*([^;]+)', webpage, 'VOD data', default='{}'),
            video_id)

        season_list = list(program_info.get('seasonList', {}).values())
        playlist_id = traverse_obj(season_list, 0, 'contentId')
        if self._yes_playlist(playlist_id, video_id, smuggled_data):
            return self._extract_playlist(season_list[0], video_id, program_info)

        # In browsers `getMainUrl` request is always issued. Usually this
        # endpoint gives the same result as the data embedded in the webpage.
        # If georestricted, there are no embedded data, so an extra request is
        # necessary to get the error code
        if 'assetId' not in program_info:
            program_info = self._download_json(
                'https://www.litv.tv/vod/ajax/getProgramInfo', video_id,
                query={'contentId': video_id},
                headers={'Accept': 'application/json'})
        video_data = self._parse_json(self._search_regex(
            r'uiHlsUrl\s*=\s*testBackendData\(([^;]+)\);',
            webpage, 'video data', default='{}'), video_id)
        if not video_data:
            payload = {
                'assetId': program_info['assetId'],
                'watchDevices': program_info['watchDevices'],
                'contentType': program_info['contentType'],
            }
            video_data = self._download_json(
                'https://www.litv.tv/vod/getMainUrl', video_id,
                data=json.dumps(payload).encode('utf-8'),
                headers={'Content-Type': 'application/json'})

        if not video_data.get('fullpath'):
            error_msg = video_data.get('errorMessage')
            if error_msg == 'vod.error.outsideregionerror':
                self.raise_geo_restricted('This video is available in Taiwan only')
            if error_msg:
                raise ExtractorError('%s said: %s' % (self.IE_NAME, error_msg), expected=True)
            raise ExtractorError('Unexpected result from %s' % self.IE_NAME)

        formats = self._extract_m3u8_formats(
            video_data['fullpath'], video_id, ext='mp4',
            entry_protocol='m3u8_native', m3u8_id='hls')
        for a_format in formats:
            # LiTV HLS segments doesn't like compressions
            a_format.setdefault('http_headers', {})['Youtubedl-no-compression'] = True

        title = program_info['title'] + program_info.get('secondaryMark', '')
        description = program_info.get('description')
        thumbnail = program_info.get('imageFile')
        categories = [item['name'] for item in program_info.get('category', [])]
        episode = int_or_none(program_info.get('episode'))

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


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

class LiveJournalIE(InfoExtractor):
    _VALID_URL = r'https?://(?:[^.]+\.)?livejournal\.com/video/album/\d+.+?\bid=(?P<id>\d+)'
    _TEST = {
        'url': 'https://andrei-bt.livejournal.com/video/album/407/?mode=view&id=51272',
        'md5': 'adaf018388572ced8a6f301ace49d4b2',
        'info_dict': {
            'id': '1263729',
            'ext': 'mp4',
            'title': 'Истребители против БПЛА',
            'upload_date': '20190624',
            'timestamp': 1561406715,
        }
    }

    def _real_extract(self, url):
        video_id = self._match_id(url)
        webpage = self._download_webpage(url, video_id)
        record = self._parse_json(self._search_regex(
            r'Site\.page\s*=\s*({.+?});', webpage,
            'page data'), video_id)['video']['record']
        storage_id = compat_str(record['storageid'])
        title = record.get('name')
        if title:
            # remove filename extension(.mp4, .mov, etc...)
            title = title.rsplit('.', 1)[0]
        return {
            '_type': 'url_transparent',
            'id': video_id,
            'title': title,
            'thumbnail': record.get('thumbnail'),
            'timestamp': int_or_none(record.get('timecreate')),
            'url': 'eagleplatform:vc.videos.livejournal.com:' + storage_id,
            'ie_key': 'EaglePlatform',
        }