code:
from .common import InfoExtractor

class SexuIE(InfoExtractor):
    _VALID_URL = r'https?://(?:www\.)?sexu\.com/(?P<id>\d+)'
    _TEST = {
        'url': 'http://sexu.com/961791/',
        'md5': 'ff615aca9691053c94f8f10d96cd7884',
        'info_dict': {
            'id': '961791',
            'ext': 'mp4',
            'title': 'md5:4d05a19a5fc049a63dbbaf05fb71d91b',
            'description': 'md5:2b75327061310a3afb3fbd7d09e2e403',
            'categories': list,  # NSFW
            'thumbnail': r're:https?://.*\.jpg$',
            'age_limit': 18,
        }
    }

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

        jwvideo = self._parse_json(
            self._search_regex(r'\.setup\(\s*({.+?})\s*\);', webpage, 'jwvideo'),
            video_id)

        sources = jwvideo['sources']

        formats = [{
            'url': source['file'].replace('\\', ''),
            'format_id': source.get('label'),
            'height': int(self._search_regex(
                r'^(\d+)[pP]', source.get('label', ''), 'height',
                default=None)),
        } for source in sources if source.get('file')]
        self._sort_formats(formats)

        title = self._html_search_regex(
            r'<title>([^<]+)\s*-\s*Sexu\.Com</title>', webpage, 'title')

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

        thumbnail = jwvideo.get('image')

        categories_str = self._html_search_meta(
            'keywords', webpage, 'categories')
        categories = (
            None if categories_str is None
            else categories_str.split(','))

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


code:
import json
import math
import re

from .aws import AWSIE
from ..compat import compat_HTTPError
from ..utils import (
    clean_html,
    ExtractorError,
    InAdvancePagedList,
    int_or_none,
    parse_iso8601,
    str_or_none,
    urlencode_postdata,
)

class ShahidBaseIE(AWSIE):
    _AWS_PROXY_HOST = 'api2.shahid.net'
    _AWS_API_KEY = '2RRtuMHx95aNI1Kvtn2rChEuwsCogUd4samGPjLh'
    _VALID_URL_BASE = r'https?://shahid\.mbc\.net/[a-z]{2}/'

    def _handle_error(self, e):
        fail_data = self._parse_json(
            e.cause.read().decode('utf-8'), None, fatal=False)
        if fail_data:
            faults = fail_data.get('faults', [])
            faults_message = ', '.join([clean_html(fault['userMessage']) for fault in faults if fault.get('userMessage')])
            if faults_message:
                raise ExtractorError(faults_message, expected=True)

    def _call_api(self, path, video_id, request=None):
        query = {}
        if request:
            query['request'] = json.dumps(request)
        try:
            return self._aws_execute_api({
                'uri': '/proxy/v2/' + path,
                'access_key': 'AKIAI6X4TYCIXM2B7MUQ',
                'secret_key': '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',
            }, video_id, query)
        except ExtractorError as e:
            if isinstance(e.cause, compat_HTTPError):
                self._handle_error(e)
            raise

class ShahidIE(ShahidBaseIE):
    _NETRC_MACHINE = 'shahid'
    _VALID_URL = ShahidBaseIE._VALID_URL_BASE + r'(?:serie|show|movie)s/[^/]+/(?P<type>episode|clip|movie)-(?P<id>\d+)'
    _TESTS = [{
        'url': 'https://shahid.mbc.net/ar/shows/%D9%85%D8%AA%D8%AD%D9%81-%D8%A7%D9%84%D8%AF%D8%AD%D9%8A%D8%AD-%D8%A7%D9%84%D9%85%D9%88%D8%B3%D9%85-1-%D9%83%D9%84%D9%8A%D8%A8-1/clip-816924',
        'info_dict': {
            'id': '816924',
            'ext': 'mp4',
            'title': 'متحف الدحيح الموسم 1 كليب 1',
            'timestamp': 1602806400,
            'upload_date': '20201016',
            'description': 'برومو',
            'duration': 22,
            'categories': ['كوميديا'],
        },
        'params': {
            # m3u8 download
            'skip_download': True,
        }
    }, {
        'url': 'https://shahid.mbc.net/ar/movies/%D8%A7%D9%84%D9%82%D9%86%D8%A7%D8%B5%D8%A9/movie-151746',
        'only_matching': True
    }, {
        # shahid plus subscriber only
        'url': 'https://shahid.mbc.net/ar/series/%D9%85%D8%B1%D8%A7%D9%8A%D8%A7-2011-%D8%A7%D9%84%D9%85%D9%88%D8%B3%D9%85-1-%D8%A7%D9%84%D8%AD%D9%84%D9%82%D8%A9-1/episode-90511',
        'only_matching': True
    }, {
        'url': 'https://shahid.mbc.net/en/shows/Ramez-Fi-Al-Shallal-season-1-episode-1/episode-359319',
        'only_matching': True
    }]

    def _perform_login(self, username, password):
        try:
            user_data = self._download_json(
                'https://shahid.mbc.net/wd/service/users/login',
                None, 'Logging in', data=json.dumps({
                    'email': username,
                    'password': password,
                    'basic': 'false',
                }).encode('utf-8'), headers={
                    'Content-Type': 'application/json; charset=UTF-8',
                })['user']
        except ExtractorError as e:
            if isinstance(e.cause, compat_HTTPError):
                self._handle_error(e)
            raise

        self._download_webpage(
            'https://shahid.mbc.net/populateContext',
            None, 'Populate Context', data=urlencode_postdata({
                'firstName': user_data['firstName'],
                'lastName': user_data['lastName'],
                'userName': user_data['email'],
                'csg_user_name': user_data['email'],
                'subscriberId': user_data['id'],
                'sessionId': user_data['sessionId'],
            }))

    def _real_extract(self, url):
        page_type, video_id = self._match_valid_url(url).groups()
        if page_type == 'clip':
            page_type = 'episode'

        playout = self._call_api(
            'playout/new/url/' + video_id, video_id)['playout']

        if not self.get_param('allow_unplayable_formats') and playout.get('drm'):
            self.report_drm(video_id)

        formats = self._extract_m3u8_formats(re.sub(
            # https://docs.aws.amazon.com/mediapackage/latest/ug/manifest-filtering.html
            r'aws\.manifestfilter=[\w:;,-]+&?',
            '', playout['url']), video_id, 'mp4')
        self._sort_formats(formats)

        # video = self._call_api(
        #     'product/id', video_id, {
        #         'id': video_id,
        #         'productType': 'ASSET',
        #         'productSubType': page_type.upper()
        #     })['productModel']

        response = self._download_json(
            'http://api.shahid.net/api/v1_1/%s/%s' % (page_type, video_id),
            video_id, 'Downloading video JSON', query={
                'apiKey': 'sh@hid0nlin3',
                'hash': 'b2wMCTHpSmyxGqQjJFOycRmLSex+BpTK/ooxy6vHaqs=',
            })
        data = response.get('data', {})
        error = data.get('error')
        if error:
            raise ExtractorError(
                '%s returned error: %s' % (self.IE_NAME, '\n'.join(error.values())),
                expected=True)

        video = data[page_type]
        title = video['title']
        categories = [
            category['name']
            for category in video.get('genres', []) if 'name' in category]

        return {
            'id': video_id,
            'title': title,
            'description': video.get('description'),
            'thumbnail': video.get('thumbnailUrl'),
            'duration': int_or_none(video.get('duration')),
            'timestamp': parse_iso8601(video.get('referenceDate')),
            'categories': categories,
            'series': video.get('showTitle') or video.get('showName'),
            'season': video.get('seasonTitle'),
            'season_number': int_or_none(video.get('seasonNumber')),
            'season_id': str_or_none(video.get('seasonId')),
            'episode_number': int_or_none(video.get('number')),
            'episode_id': video_id,
            'formats': formats,
        }

class ShahidShowIE(ShahidBaseIE):
    _VALID_URL = ShahidBaseIE._VALID_URL_BASE + r'(?:show|serie)s/[^/]+/(?:show|series)-(?P<id>\d+)'
    _TESTS = [{
        'url': 'https://shahid.mbc.net/ar/shows/%D8%B1%D8%A7%D9%85%D8%B2-%D9%82%D8%B1%D8%B4-%D8%A7%D9%84%D8%A8%D8%AD%D8%B1/show-79187',
        'info_dict': {
            'id': '79187',
            'title': 'رامز قرش البحر',
            'description': 'md5:c88fa7e0f02b0abd39d417aee0d046ff',
        },
        'playlist_mincount': 32,
    }, {
        'url': 'https://shahid.mbc.net/ar/series/How-to-live-Longer-(The-Big-Think)/series-291861',
        'only_matching': True
    }]
    _PAGE_SIZE = 30

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

        product = self._call_api(
            'playableAsset', show_id, {'showId': show_id})['productModel']
        playlist = product['playlist']
        playlist_id = playlist['id']
        show = product.get('show', {})

        def page_func(page_num):
            playlist = self._call_api(
                'product/playlist', show_id, {
                    'playListId': playlist_id,
                    'pageNumber': page_num,
                    'pageSize': 30,
                    'sorts': [{
                        'order': 'DESC',
                        'type': 'SORTDATE'
                    }],
                })
            for product in playlist.get('productList', {}).get('products', []):
                product_url = product.get('productUrl', []).get('url')
                if not product_url:
                    continue
                yield self.url_result(
                    product_url, 'Shahid',
                    str_or_none(product.get('id')),
                    product.get('title'))

        entries = InAdvancePagedList(
            page_func,
            math.ceil(playlist['count'] / self._PAGE_SIZE),
            self._PAGE_SIZE)

        return self.playlist_result(
            entries, show_id, show.get('title'), show.get('description'))

repo_name: Lesmiscore/ytdl


code:
import urllib.parse

from .common import InfoExtractor
from ..compat import compat_b64decode
from ..utils import (
    KNOWN_EXTENSIONS,
    ExtractorError,
    determine_ext,
    int_or_none,
    js_to_json,
    parse_filesize,
    rot47,
    url_or_none,
    urlencode_postdata,
)

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

        webpage, urlh = self._download_webpage_handle(url, video_id)

        if self._FILE_NOT_FOUND in webpage:
            raise ExtractorError(
                'Video %s does not exist' % video_id, expected=True)

        video_url = self._extract_video_url(webpage, video_id, url)

        title = self._extract_title(webpage)
        filesize = int_or_none(self._extract_filesize(webpage))

        return {
            'id': video_id,
            'url': video_url,
            'ext': 'mp4',
            'filesize': filesize,
            'title': title,
        }

    def _extract_title(self, webpage):
        return compat_b64decode(self._html_search_meta(
            'full:title', webpage, 'title')).decode('utf-8')

    def _extract_filesize(self, webpage):
        return self._html_search_meta(
            'full:size', webpage, 'file size', fatal=False)

class SharedIE(SharedBaseIE):
    IE_DESC = 'shared.sx'
    _VALID_URL = r'https?://shared\.sx/(?P<id>[\da-z]{10})'
    _FILE_NOT_FOUND = '>File does not exist<'

    _TEST = {
        'url': 'http://shared.sx/0060718775',
        'md5': '106fefed92a8a2adb8c98e6a0652f49b',
        'info_dict': {
            'id': '0060718775',
            'ext': 'mp4',
            'title': 'Bmp4',
            'filesize': 1720110,
        },
    }

    def _extract_video_url(self, webpage, video_id, url):
        download_form = self._hidden_inputs(webpage)

        video_page = self._download_webpage(
            url, video_id, 'Downloading video page',
            data=urlencode_postdata(download_form),
            headers={
                'Content-Type': 'application/x-www-form-urlencoded',
                'Referer': url,
            })

        video_url = self._html_search_regex(
            r'data-url=(["\'])(?P<url>(?:(?!\1).)+)\1',
            video_page, 'video URL', group='url')

        return video_url

class VivoIE(SharedBaseIE):
    IE_DESC = 'vivo.sx'
    _VALID_URL = r'https?://vivo\.s[xt]/(?P<id>[\da-z]{10})'
    _FILE_NOT_FOUND = '>The file you have requested does not exists or has been removed'

    _TESTS = [{
        'url': 'http://vivo.sx/d7ddda0e78',
        'md5': '15b3af41be0b4fe01f4df075c2678b2c',
        'info_dict': {
            'id': 'd7ddda0e78',
            'ext': 'mp4',
            'title': 'Chicken',
            'filesize': 515659,
        },
    }, {
        'url': 'http://vivo.st/d7ddda0e78',
        'only_matching': True,
    }]

    def _extract_title(self, webpage):
        title = self._html_search_regex(
            r'data-name\s*=\s*(["\'])(?P<title>(?:(?!\1).)+)\1', webpage,
            'title', default=None, group='title')
        if title:
            ext = determine_ext(title)
            if ext.lower() in KNOWN_EXTENSIONS:
                title = title.rpartition('.' + ext)[0]
            return title
        return self._og_search_title(webpage)

    def _extract_filesize(self, webpage):
        return parse_filesize(self._search_regex(
            r'data-type=["\']video["\'][^>]*>Watch.*?<strong>\s*\((.+?)\)',
            webpage, 'filesize', fatal=False))

    def _extract_video_url(self, webpage, video_id, url):
        def decode_url_old(encoded_url):
            return compat_b64decode(encoded_url).decode('utf-8')

        stream_url = self._search_regex(
            r'data-stream\s*=\s*(["\'])(?P<url>(?:(?!\1).)+)\1', webpage,
            'stream url', default=None, group='url')
        if stream_url:
            stream_url = url_or_none(decode_url_old(stream_url))
        if stream_url:
            return stream_url

        def decode_url(encoded_url):
            return rot47(urllib.parse.unquote_plus(encoded_url))

        return decode_url(self._parse_json(
            self._search_regex(
                r'(?s)InitializeStream\s*\(\s*({.+?})\s*\)\s*;', webpage,
                'stream'),
            video_id, transform_source=js_to_json)['source'])


code:
from ..utils import KNOWN_EXTENSIONS, determine_ext, mimetype2ext, try_get
from .common import InfoExtractor

class ShareVideosEmbedIE(InfoExtractor):
    IE_NAME = 'sharevideos'
    _VALID_URL = r'https?://(?:embed\.)?share-videos\.se/auto/(?:embed|video)/(?P<id>\d+)\?uid=(?P<uid>\d+)'
    _EMBED_REGEX = [r'<iframe[^>]+?\bsrc\s*=\s*(["\'])(?P<url>(?:https?:)?//embed\.share-videos\.se/auto/embed/\d+\?.*?\buid=\d+.*?)\1']
    TEST = {
        'url': 'http://share-videos.se/auto/video/83645793?uid=13',
        'md5': 'b68d276de422ab07ee1d49388103f457',
        'info_dict': {
            'id': '83645793',
            'title': 'Lock up and get excited',
            'ext': 'mp4'
        },
        'skip': 'TODO: fix nested playlists processing in tests',
    }

    def _real_extract(self, url):
        mobj = self._match_valid_url(url)
        video_id, uid = mobj.group('id', 'uid')
        webpage = self._download_webpage(f'https://embed.share-videos.se/auto/embed/{video_id}?uid={uid}', video_id)

        title = self._html_extract_title(webpage, 'video title', default=None)
        if not title:
            video_webpage = self._download_webpage(
                f'https://share-videos.se/auto/video/{video_id}?uid={uid}',
                video_id)
            title = self._html_extract_title(video_webpage, 'video title', default=None)
        if not title:
            tags = self._download_json(
                'https://search.share-videos.se/json/movie_tag?svid=%s&site=sv' % video_id,
                video_id, note='Giving it a better name', fatal=None)
            if tags and isinstance(tags, (list, tuple)):
                title = ' '.join(tags)
        if not title:
            self.report_warning('There is no title candidate for this video', video_id)

        def extract_mp4_url(x):
            src = self._search_regex(r'random_file\.push\("(.+)"\);', webpage, 'video url')
            src_type = self._search_regex(r'player.src\({type: \'(.+);\',', webpage, 'video type', fatal=False, default='video/mp4')
            ext = mimetype2ext(src_type) or determine_ext(src).lower()
            return {
                'formats': [{
                    'url': src,
                    'ext': ext if ext in KNOWN_EXTENSIONS else 'mp4',
                }]
            }

        entry = try_get(webpage, (
            lambda x: self._parse_html5_media_entries(url, webpage, video_id, m3u8_id='hls')[0],
            extract_mp4_url,
        ), None)
        self._sort_formats(entry['formats'])
        entry.update({
            'id': video_id,
            'title': title,
        })
        return entry


code:
from .common import InfoExtractor
from ..aes import aes_cbc_decrypt, unpad_pkcs7
from ..compat import (
    compat_b64decode,
)
from ..utils import (
    bytes_to_intlist,
    ExtractorError,
    intlist_to_bytes,
    unified_strdate,
)

class ShemarooMeIE(InfoExtractor):
    _VALID_URL = r'https?://(?:www\.)?shemaroome\.com/(?:movies|shows)/(?P<id>[^?#]+)'
    _TESTS = [{
        'url': 'https://www.shemaroome.com/movies/dil-hai-tumhaara',
        'info_dict': {
            'id': 'dil-hai-tumhaara',
            'ext': 'mp4',
            'title': 'Dil Hai Tumhaara',
            'release_date': '20020906',
            'thumbnail': r're:^https?://.*\.jpg$',
            'description': 'md5:2782c4127807103cf5a6ae2ca33645ce',
        },
        'params': {
            'skip_download': True
        }
    }, {
        'url': 'https://www.shemaroome.com/shows/jurm-aur-jazbaat/laalach',
        'info_dict': {
            'id': 'jurm-aur-jazbaat_laalach',
            'ext': 'mp4',
            'title': 'Laalach',
            'description': 'md5:92b79c2dcb539b0ab53f9fa5a048f53c',
            'thumbnail': r're:^https?://.*\.jpg$',
            'release_date': '20210507',
        },
        'params': {
            'skip_download': True
        },
        'skip': 'Premium videos cannot be downloaded yet.'
    }, {
        'url': 'https://www.shemaroome.com/shows/jai-jai-jai-bajrang-bali/jai-jai-jai-bajrang-bali-episode-99',
        'info_dict': {
            'id': 'jai-jai-jai-bajrang-bali_jai-jai-jai-bajrang-bali-episode-99',
            'ext': 'mp4',
            'title': 'Jai Jai Jai Bajrang Bali Episode 99',
            'description': 'md5:850d127a18ee3f9529d7fbde2f49910d',
            'thumbnail': r're:^https?://.*\.jpg$',
            'release_date': '20110101',
        },
        'params': {
            'skip_download': True
        }
    }]

    def _real_extract(self, url):
        video_id = self._match_id(url).replace('/', '_')
        webpage = self._download_webpage(url, video_id)
        title = self._search_regex(r'id=\"ma_title\" value=\"([^\"]+)', webpage, 'title')
        thumbnail = self._og_search_thumbnail(webpage)
        content_def = self._search_regex(r'id=\"content_definition\" value=\"([^\"]+)', webpage, 'content_def')
        catalog_id = self._search_regex(r'id=\"catalog_id\" value=\"([^\"]+)', webpage, 'catalog_id')
        item_category = self._search_regex(r'id=\"item_category\" value=\"([^\"]+)', webpage, 'item_category')
        content_id = self._search_regex(r'id=\"content_id\" value=\"([^\"]+)', webpage, 'content_id')

        data = f'catalog_id={catalog_id}&content_id={content_id}&category={item_category}&content_def={content_def}'
        data_json = self._download_json('https://www.shemaroome.com/users/user_all_lists', video_id, data=data.encode())
        if not data_json.get('status'):
            raise ExtractorError('Premium videos cannot be downloaded yet.', expected=True)
        url_data = bytes_to_intlist(compat_b64decode(data_json['new_play_url']))
        key = bytes_to_intlist(compat_b64decode(data_json['key']))
        iv = [0] * 16
        m3u8_url = unpad_pkcs7(intlist_to_bytes(aes_cbc_decrypt(url_data, key, iv))).decode('ascii')
        formats, m3u8_subs = self._extract_m3u8_formats_and_subtitles(m3u8_url, video_id, fatal=False, headers={'stream_key': data_json['stream_key']})
        self._sort_formats(formats)

        release_date = self._html_search_regex(
            (r'itemprop="uploadDate">\s*([\d-]+)', r'id="release_date" value="([\d-]+)'),
            webpage, 'release date', fatal=False)

        subtitles = {}
        sub_url = data_json.get('subtitle')
        if sub_url:
            subtitles.setdefault('EN', []).append({
                'url': self._proto_relative_url(sub_url),
            })
        subtitles = self._merge_subtitles(subtitles, m3u8_subs)
        description = self._html_search_regex(r'(?s)>Synopsis(</.+?)</', webpage, 'description', fatal=False)

        return {
            'id': video_id,
            'formats': formats,
            'title': title,
            'thumbnail': thumbnail,
            'release_date': unified_strdate(release_date),
            'description': description,
            'subtitles': subtitles,
        }


code:
from .common import InfoExtractor
from ..compat import compat_str
from ..utils import (
    ExtractorError,
    int_or_none,
    urljoin,
)

class ShowRoomLiveIE(InfoExtractor):
    _VALID_URL = r'https?://(?:www\.)?showroom-live\.com/(?!onlive|timetable|event|campaign|news|ranking|room)(?P<id>[^/?#&]+)'
    _TEST = {
        'url': 'https://www.showroom-live.com/48_Nana_Okada',
        'only_matching': True,
    }

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

        webpage = self._download_webpage(url, broadcaster_id)

        room_id = self._search_regex(
            (r'SrGlobal\.roomId\s*=\s*(\d+)',
             r'(?:profile|room)\?room_id\=(\d+)'), webpage, 'room_id')

        room = self._download_json(
            urljoin(url, '/api/room/profile?room_id=%s' % room_id),
            broadcaster_id)

        is_live = room.get('is_onlive')
        if is_live is not True:
            raise ExtractorError('%s is offline' % broadcaster_id, expected=True)

        uploader = room.get('performer_name') or broadcaster_id
        title = room.get('room_name') or room.get('main_name') or uploader

        streaming_url_list = self._download_json(
            urljoin(url, '/api/live/streaming_url?room_id=%s' % room_id),
            broadcaster_id)['streaming_url_list']

        formats = []
        for stream in streaming_url_list:
            stream_url = stream.get('url')
            if not stream_url:
                continue
            stream_type = stream.get('type')
            if stream_type == 'hls':
                m3u8_formats = self._extract_m3u8_formats(
                    stream_url, broadcaster_id, ext='mp4', m3u8_id='hls',
                    live=True)
                for f in m3u8_formats:
                    f['quality'] = int_or_none(stream.get('quality', 100))
                formats.extend(m3u8_formats)
            elif stream_type == 'rtmp':
                stream_name = stream.get('stream_name')
                if not stream_name:
                    continue
                formats.append({
                    'url': stream_url,
                    'play_path': stream_name,
                    'page_url': url,
                    'player_url': 'https://www.showroom-live.com/assets/swf/v3/ShowRoomLive.swf',
                    'rtmp_live': True,
                    'ext': 'flv',
                    'format_id': 'rtmp',
                    'format_note': stream.get('label'),
                    'quality': int_or_none(stream.get('quality', 100)),
                })
        self._sort_formats(formats)

        return {
            'id': compat_str(room.get('live_id') or broadcaster_id),
            'title': title,
            'description': room.get('description'),
            'timestamp': int_or_none(room.get('current_live_started_at')),
            'uploader': uploader,
            'uploader_id': broadcaster_id,
            'view_count': int_or_none(room.get('view_num')),
            'formats': formats,
            'is_live': True,
        }


code:
from .common import InfoExtractor
from ..utils import (
    clean_podcast_url,
    int_or_none,
    parse_iso8601,
    strip_or_none,
    try_get,
    urlencode_postdata,
)

class SimplecastBaseIE(InfoExtractor):
    _UUID_REGEX = r'[\da-f]{8}-(?:[\da-f]{4}-){3}[\da-f]{12}'
    _API_BASE = 'https://api.simplecast.com/'

    def _call_api(self, path_tmpl, video_id):
        return self._download_json(
            self._API_BASE + path_tmpl % video_id, video_id)

    def _call_search_api(self, resource, resource_id, resource_url):
        return self._download_json(
            'https://api.simplecast.com/%ss/search' % resource, resource_id,
            data=urlencode_postdata({'url': resource_url}))

    def _parse_episode(self, episode):
        episode_id = episode['id']
        title = episode['title'].strip()
        audio_file = episode.get('audio_file') or {}
        audio_file_url = audio_file.get('url') or episode.get('audio_file_url') or episode['enclosure_url']

        season = episode.get('season') or {}
        season_href = season.get('href')
        season_id = None
        if season_href:
            season_id = self._search_regex(
                r'https?://api.simplecast.com/seasons/(%s)' % self._UUID_REGEX,
                season_href, 'season id', default=None)

        webpage_url = episode.get('episode_url')
        channel_url = None
        if webpage_url:
            channel_url = self._search_regex(
                r'(https?://[^/]+\.simplecast\.com)',
                webpage_url, 'channel url', default=None)

        return {
            'id': episode_id,
            'display_id': episode.get('slug'),
            'title': title,
            'url': clean_podcast_url(audio_file_url),
            'webpage_url': webpage_url,
            'channel_url': channel_url,
            'series': try_get(episode, lambda x: x['podcast']['title']),
            'season_number': int_or_none(season.get('number')),
            'season_id': season_id,
            'thumbnail': episode.get('image_url'),
            'episode_id': episode_id,
            'episode_number': int_or_none(episode.get('number')),
            'description': strip_or_none(episode.get('description')),
            'timestamp': parse_iso8601(episode.get('published_at')),
            'duration': int_or_none(episode.get('duration')),
            'filesize': int_or_none(audio_file.get('size') or episode.get('audio_file_size')),
        }

class SimplecastIE(SimplecastBaseIE):
    IE_NAME = 'simplecast'
    _VALID_URL = r'https?://(?:api\.simplecast\.com/episodes|player\.simplecast\.com)/(?P<id>%s)' % SimplecastBaseIE._UUID_REGEX
    _EMBED_REGEX = [rf'''(?x)<iframe[^>]+src=["\']
        (?P<url>https?://(?:
            embed\.simplecast\.com/[0-9a-f]{8}|
            player\.simplecast\.com/{SimplecastBaseIE._UUID_REGEX}
        ))''']
    _COMMON_TEST_INFO = {
        'display_id': 'errant-signal-chris-franklin-new-wave-video-essays',
        'id': 'b6dc49a2-9404-4853-9aa9-9cfc097be876',
        'ext': 'mp3',
        'title': 'Errant Signal - Chris Franklin & New Wave Video Essays',
        'episode_number': 1,
        'episode_id': 'b6dc49a2-9404-4853-9aa9-9cfc097be876',
        'description': 'md5:34752789d3d2702e2d2c975fbd14f357',
        'season_number': 1,
        'season_id': 'e23df0da-bae4-4531-8bbf-71364a88dc13',
        'series': 'The RE:BIND.io Podcast',
        'duration': 5343,
        'timestamp': 1580979475,
        'upload_date': '20200206',
        'webpage_url': r're:^https?://the-re-bind-io-podcast\.simplecast\.com/episodes/errant-signal-chris-franklin-new-wave-video-essays',
        'channel_url': r're:^https?://the-re-bind-io-podcast\.simplecast\.com$',
    }
    _TESTS = [{
        'url': 'https://api.simplecast.com/episodes/b6dc49a2-9404-4853-9aa9-9cfc097be876',
        'md5': '8c93be7be54251bf29ee97464eabe61c',
        'info_dict': _COMMON_TEST_INFO,
    }, {
        'url': 'https://player.simplecast.com/b6dc49a2-9404-4853-9aa9-9cfc097be876',
        'only_matching': True,
    }]

    def _real_extract(self, url):
        episode_id = self._match_id(url)
        episode = self._call_api('episodes/%s', episode_id)
        return self._parse_episode(episode)

class SimplecastEpisodeIE(SimplecastBaseIE):
    IE_NAME = 'simplecast:episode'
    _VALID_URL = r'https?://(?!api\.)[^/]+\.simplecast\.com/episodes/(?P<id>[^/?&#]+)'
    _TEST = {
        'url': 'https://the-re-bind-io-podcast.simplecast.com/episodes/errant-signal-chris-franklin-new-wave-video-essays',
        'md5': '8c93be7be54251bf29ee97464eabe61c',
        'info_dict': SimplecastIE._COMMON_TEST_INFO,
    }

    def _real_extract(self, url):
        mobj = self._match_valid_url(url)
        episode = self._call_search_api(
            'episode', mobj.group(1), mobj.group(0))
        return self._parse_episode(episode)

class SimplecastPodcastIE(SimplecastBaseIE):
    IE_NAME = 'simplecast:podcast'
    _VALID_URL = r'https?://(?!(?:api|cdn|embed|feeds|player)\.)(?P<id>[^/]+)\.simplecast\.com(?!/episodes/[^/?&#]+)'
    _TESTS = [{
        'url': 'https://the-re-bind-io-podcast.simplecast.com',
        'playlist_mincount': 33,
        'info_dict': {
            'id': '07d28d26-7522-42eb-8c53-2bdcfc81c43c',
            'title': 'The RE:BIND.io Podcast',
        },
    }, {
        'url': 'https://the-re-bind-io-podcast.simplecast.com/episodes',
        'only_matching': True,
    }]

    def _real_extract(self, url):
        subdomain = self._match_id(url)
        site = self._call_search_api('site', subdomain, url)
        podcast = site['podcast']
        podcast_id = podcast['id']
        podcast_title = podcast.get('title')

        def entries():
            episodes = self._call_api('podcasts/%s/episodes', podcast_id)
            for episode in (episodes.get('collection') or []):
                info = self._parse_episode(episode)
                info['series'] = podcast_title
                yield info

        return self.playlist_result(entries(), podcast_id, podcast_title)


code:
from .common import InfoExtractor
from ..utils import (
    HEADRequest,
    ExtractorError,
    int_or_none,
    update_url_query,
    qualities,
    get_element_by_attribute,
    clean_html,
)

class SinaIE(InfoExtractor):
    _VALID_URL = r'''(?x)https?://(?:.*?\.)?video\.sina\.com\.cn/
                        (?:
                            (?:view/|.*\#)(?P<id>\d+)|
                            .+?/(?P<pseudo_id>[^/?#]+)(?:\.s?html)|
                            # This is used by external sites like Weibo
                            api/sinawebApi/outplay.php/(?P<token>.+?)\.swf
                        )
                  '''

    _TESTS = [
        {
            'url': 'http://video.sina.com.cn/news/spj/topvideoes20160504/?opsubject_id=top1#250576622',
            'md5': 'd38433e2fc886007729735650ae4b3e9',
            'info_dict': {
                'id': '250576622',
                'ext': 'mp4',
                'title': '现场:克鲁兹宣布退选 特朗普将稳获提名',
            }
        },
        {
            'url': 'http://video.sina.com.cn/v/b/101314253-1290078633.html',
            'info_dict': {
                'id': '101314253',
                'ext': 'flv',
                'title': '军方提高对朝情报监视级别',
            },
            'skip': 'the page does not exist or has been deleted',
        },
        {
            'url': 'http://video.sina.com.cn/view/250587748.html',
            'md5': '3d1807a25c775092aab3bc157fff49b4',
            'info_dict': {
                'id': '250587748',
                'ext': 'mp4',
                'title': '瞬间泪目：8年前汶川地震珍贵视频首曝光',
            },
        },
    ]

    def _real_extract(self, url):
        mobj = self._match_valid_url(url)

        video_id = mobj.group('id')
        if not video_id:
            if mobj.group('token') is not None:
                # The video id is in the redirected url
                self.to_screen('Getting video id')
                request = HEADRequest(url)
                _, urlh = self._download_webpage_handle(request, 'NA', False)
                return self._real_extract(urlh.geturl())
            else:
                pseudo_id = mobj.group('pseudo_id')
                webpage = self._download_webpage(url, pseudo_id)
                error = get_element_by_attribute('class', 'errtitle', webpage)
                if error:
                    raise ExtractorError('%s said: %s' % (
                        self.IE_NAME, clean_html(error)), expected=True)
                video_id = self._search_regex(
                    r"video_id\s*:\s*'(\d+)'", webpage, 'video id')

        video_data = self._download_json(
            'http://s.video.sina.com.cn/video/h5play',
            video_id, query={'video_id': video_id})
        if video_data['code'] != 1:
            raise ExtractorError('%s said: %s' % (
                self.IE_NAME, video_data['message']), expected=True)
        else:
            video_data = video_data['data']
            title = video_data['title']
            description = video_data.get('description')
            if description:
                description = description.strip()

            preference = qualities(['cif', 'sd', 'hd', 'fhd', 'ffd'])
            formats = []
            for quality_id, quality in video_data.get('videos', {}).get('mp4', {}).items():
                file_api = quality.get('file_api')
                file_id = quality.get('file_id')
                if not file_api or not file_id:
                    continue
                formats.append({
                    'format_id': quality_id,
                    'url': update_url_query(file_api, {'vid': file_id}),
                    'quality': preference(quality_id),
                    'ext': 'mp4',
                })
            self._sort_formats(formats)

            return {
                'id': video_id,
                'title': title,
                'description': description,
                'thumbnail': video_data.get('image'),
                'duration': int_or_none(video_data.get('length')),
                'timestamp': int_or_none(video_data.get('create_time')),
                'formats': formats,
            }


code:
from .common import InfoExtractor
from ..compat import (
    compat_str,
)
from ..utils import (
    determine_ext,
    int_or_none,
    parse_qs,
    try_get,
    qualities,
)

class SixPlayIE(InfoExtractor):
    IE_NAME = '6play'
    _VALID_URL = r'(?:6play:|https?://(?:www\.)?(?P<domain>6play\.fr|rtlplay\.be|play\.rtl\.hr|rtlmost\.hu)/.+?-c_)(?P<id>[0-9]+)'
    _TESTS = [{
        'url': 'https://www.6play.fr/minute-par-minute-p_9533/le-but-qui-a-marque-lhistoire-du-football-francais-c_12041051',
        'md5': '31fcd112637baa0c2ab92c4fcd8baf27',
        'info_dict': {
            'id': '12041051',
            'ext': 'mp4',
            'title': 'Le but qui a marqué l\'histoire du football français !',
            'description': 'md5:b59e7e841d646ef1eb42a7868eb6a851',
        },
    }, {
        'url': 'https://www.rtlplay.be/rtl-info-13h-p_8551/les-titres-du-rtlinfo-13h-c_12045869',
        'only_matching': True,
    }, {
        'url': 'https://play.rtl.hr/pj-masks-p_9455/epizoda-34-sezona-1-catboyevo-cudo-na-dva-kotaca-c_11984989',
        'only_matching': True,
    }, {
        'url': 'https://www.rtlmost.hu/megtorve-p_14167/megtorve-6-resz-c_12397787',
        'only_matching': True,
    }]

    def _real_extract(self, url):
        domain, video_id = self._match_valid_url(url).groups()
        service, consumer_name = {
            '6play.fr': ('6play', 'm6web'),
            'rtlplay.be': ('rtlbe_rtl_play', 'rtlbe'),
            'play.rtl.hr': ('rtlhr_rtl_play', 'rtlhr'),
            'rtlmost.hu': ('rtlhu_rtl_most', 'rtlhu'),
        }.get(domain, ('6play', 'm6web'))

        data = self._download_json(
            'https://pc.middleware.6play.fr/6play/v2/platforms/m6group_web/services/%s/videos/clip_%s' % (service, video_id),
            video_id, headers={
                'x-customer-name': consumer_name
            }, query={
                'csa': 5,
                'with': 'clips',
            })

        clip_data = data['clips'][0]
        title = clip_data['title']

        urls = []
        quality_key = qualities(['lq', 'sd', 'hq', 'hd'])
        formats = []
        subtitles = {}
        assets = clip_data.get('assets') or []
        for asset in assets:
            asset_url = asset.get('full_physical_path')
            protocol = asset.get('protocol')
            if not asset_url or ((protocol == 'primetime' or asset.get('type') == 'usp_hlsfp_h264') and not ('_drmnp.ism/' in asset_url or '_unpnp.ism/' in asset_url)) or asset_url in urls:
                continue
            urls.append(asset_url)
            container = asset.get('video_container')
            ext = determine_ext(asset_url)
            if protocol == 'http_subtitle' or ext == 'vtt':
                subtitles.setdefault('fr', []).append({'url': asset_url})
                continue
            if container == 'm3u8' or ext == 'm3u8':
                if protocol == 'usp':
                    if parse_qs(asset_url).get('token', [None])[0]:
                        urlh = self._request_webpage(
                            asset_url, video_id, fatal=False,
                            headers=self.geo_verification_headers())
                        if not urlh:
                            continue
                        asset_url = urlh.geturl()
                    asset_url = asset_url.replace('_drmnp.ism/', '_unpnp.ism/')
                    for i in range(3, 0, -1):
                        asset_url = asset_url = asset_url.replace('_sd1/', '_sd%d/' % i)
                        m3u8_formats = self._extract_m3u8_formats(
                            asset_url, video_id, 'mp4', 'm3u8_native',
                            m3u8_id='hls', fatal=False)
                        formats.extend(m3u8_formats)
                        formats.extend(self._extract_mpd_formats(
                            asset_url.replace('.m3u8', '.mpd'),
                            video_id, mpd_id='dash', fatal=False))
                        if m3u8_formats:
                            break
                else:
                    formats.extend(self._extract_m3u8_formats(
                        asset_url, video_id, 'mp4', 'm3u8_native',
                        m3u8_id='hls', fatal=False))
            elif container == 'mp4' or ext == 'mp4':
                quality = asset.get('video_quality')
                formats.append({
                    'url': asset_url,
                    'format_id': quality,
                    'quality': quality_key(quality),
                    'ext': ext,
                })
        self._sort_formats(formats)

        def get(getter):
            for src in (data, clip_data):
                v = try_get(src, getter, compat_str)
                if v:
                    return v

        return {
            'id': video_id,
            'title': title,
            'description': get(lambda x: x['description']),
            'duration': int_or_none(clip_data.get('duration')),
            'series': get(lambda x: x['program']['title']),
            'formats': formats,
            'subtitles': subtitles,
        }


code:
from .common import InfoExtractor
from ..utils import ExtractorError, determine_ext, parse_qs, traverse_obj

class SkebIE(InfoExtractor):
    _VALID_URL = r'https?://skeb\.jp/@[^/]+/works/(?P<id>\d+)'

    _TESTS = [{
        'url': 'https://skeb.jp/@riiru_wm/works/10',
        'info_dict': {
            'id': '466853',
            'title': '内容はおまかせします！ by 姫ノ森りぃる@一周年',
            'descripion': 'md5:1ec50901efc3437cfbfe3790468d532d',
            'uploader': '姫ノ森りぃる@一周年',
            'uploader_id': 'riiru_wm',
            'age_limit': 0,
            'tags': [],
            'url': r're:https://skeb.+',
            'thumbnail': r're:https://skeb.+',
            'subtitles': {
                'jpn': [{
                    'url': r're:https://skeb.+',
                    'ext': 'vtt'
                }]
            },
            'width': 720,
            'height': 405,
            'duration': 313,
            'fps': 30,
            'ext': 'mp4',
        },
    }, {
        'url': 'https://skeb.jp/@furukawa_nob/works/3',
        'info_dict': {
            'id': '489408',
            'title': 'いつもお世話になってお... by 古川ノブ@音楽とVlo...',
            'descripion': 'md5:5adc2e41d06d33b558bf7b1faeb7b9c2',
            'uploader': '古川ノブ@音楽とVlogのVtuber',
            'uploader_id': 'furukawa_nob',
            'age_limit': 0,
            'tags': [
                'よろしく', '大丈夫', 'お願い', 'でした',
                '是非', 'O', 'バー', '遊び', 'おはよう',
                'オーバ', 'ボイス',
            ],
            'url': r're:https://skeb.+',
            'thumbnail': r're:https://skeb.+',
            'subtitles': {
                'jpn': [{
                    'url': r're:https://skeb.+',
                    'ext': 'vtt'
                }]
            },
            'duration': 98,
            'ext': 'mp3',
            'vcodec': 'none',
            'abr': 128,
        },
    }, {
        'url': 'https://skeb.jp/@mollowmollow/works/6',
        'info_dict': {
            'id': '6',
            'title': 'ヒロ。\n\n私のキャラク... by 諸々',
            'descripion': 'md5:aa6cbf2ba320b50bce219632de195f07',
            '_type': 'playlist',
            'entries': [{
                'id': '486430',
                'title': 'ヒロ。\n\n私のキャラク... by 諸々',
                'descripion': 'md5:aa6cbf2ba320b50bce219632de195f07',
            }, {
                'id': '486431',
                'title': 'ヒロ。\n\n私のキャラク... by 諸々',
            }]
        }
    }]

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

        parent = {
            'id': video_id,
            'title': nuxt_data.get('title'),
            'descripion': nuxt_data.get('description'),
            'reply': nuxt_data.get('thanks'),
            'uploader': traverse_obj(nuxt_data, ('creator', 'name')),
            'uploader_id': traverse_obj(nuxt_data, ('creator', 'screen_name')),
            'age_limit': 18 if nuxt_data.get('nsfw') else 0,
            'tags': nuxt_data.get('tag_list'),
        }

        entries = []
        for item in nuxt_data.get('previews') or []:
            vid_url = item.get('url')
            given_ext = traverse_obj(item, ('information', 'extension'))
            preview_ext = determine_ext(vid_url, default_ext=None)
            if not preview_ext:
                content_disposition = parse_qs(vid_url)['response-content-disposition'][0]
                preview_ext = self._search_regex(
                    r'filename="[^"]+\.([^\.]+?)"', content_disposition,
                    'preview file extension', fatal=False, group=1)
            if preview_ext not in ('mp4', 'mp3'):
                continue
            if not vid_url or not item.get('id'):
                continue
            width, height = traverse_obj(item, ('information', 'width')), traverse_obj(item, ('information', 'height'))
            if width is not None and height is not None:
                # the longest side is at most 720px for non-client viewers
                max_size = max(width, height)
                width, height = list(x * 720 // max_size for x in (width, height))
            entries.append({
                **parent,
                'id': str(item['id']),
                'url': vid_url,
                'thumbnail': item.get('poster_url'),
                'subtitles': {
                    'jpn': [{
                        'url': item.get('vtt_url'),
                        'ext': 'vtt',
                    }]
                } if item.get('vtt_url') else None,
                'width': width,
                'height': height,
                'duration': traverse_obj(item, ('information', 'duration')),
                'fps': traverse_obj(item, ('information', 'frame_rate')),
                'ext': preview_ext or given_ext,
                'vcodec': 'none' if preview_ext == 'mp3' else None,
                # you'll always get 128kbps MP3 for non-client viewers
                'abr': 128 if preview_ext == 'mp3' else None,
            })

        if not entries:
            raise ExtractorError('No video/audio attachment found in this commission.', expected=True)
        elif len(entries) == 1:
            return entries[0]
        else:
            parent.update({
                '_type': 'playlist',
                'entries': entries,
            })
            return parent


code:
import re

from .common import InfoExtractor
from ..utils import (
    extract_attributes,
    smuggle_url,
    strip_or_none,
    urljoin,
)

class SkyBaseIE(InfoExtractor):
    BRIGHTCOVE_URL_TEMPLATE = 'http://players.brightcove.net/%s/%s_default/index.html?videoId=%s'
    _SDC_EL_REGEX = r'(?s)(<div[^>]+data-(?:component-name|fn)="sdc-(?:articl|sit)e-video"[^>]*>)'

    def _process_ooyala_element(self, webpage, sdc_el, url):
        sdc = extract_attributes(sdc_el)
        provider = sdc.get('data-provider')
        if provider == 'ooyala':
            video_id = sdc['data-sdc-video-id']
            video_url = 'ooyala:%s' % video_id
            ie_key = 'Ooyala'
            ooyala_el = self._search_regex(
                r'(<div[^>]+class="[^"]*\bsdc-article-video__media-ooyala\b[^"]*"[^>]+data-video-id="%s"[^>]*>)' % video_id,
                webpage, 'video data', fatal=False)
            if ooyala_el:
                ooyala_attrs = extract_attributes(ooyala_el) or {}
                if ooyala_attrs.get('data-token-required') == 'true':
                    token_fetch_url = (self._parse_json(ooyala_attrs.get(
                        'data-token-fetch-options', '{}'),
                        video_id, fatal=False) or {}).get('url')
                    if token_fetch_url:
                        embed_token = self._download_json(urljoin(
                            url, token_fetch_url), video_id, fatal=False)
                        if embed_token:
                            video_url = smuggle_url(
                                video_url, {'embed_token': embed_token})
        elif provider == 'brightcove':
            video_id = sdc['data-video-id']
            account_id = sdc.get('data-account-id') or '6058004172001'
            player_id = sdc.get('data-player-id') or 'RC9PQUaJ6'
            video_url = self.BRIGHTCOVE_URL_TEMPLATE % (account_id, player_id, video_id)
            ie_key = 'BrightcoveNew'

        return {
            '_type': 'url_transparent',
            'id': video_id,
            'url': video_url,
            'ie_key': ie_key,
        }

    def _real_extract(self, url):
        video_id = self._match_id(url)
        webpage = self._download_webpage(url, video_id)
        info = self._process_ooyala_element(webpage, self._search_regex(
            self._SDC_EL_REGEX, webpage, 'sdc element'), url)
        info.update({
            'title': self._og_search_title(webpage),
            'description': strip_or_none(self._og_search_description(webpage)),
        })
        return info

class SkySportsIE(SkyBaseIE):
    IE_NAME = 'sky:sports'
    _VALID_URL = r'https?://(?:www\.)?skysports\.com/watch/video/([^/]+/)*(?P<id>[0-9]+)'
    _TESTS = [{
        'url': 'http://www.skysports.com/watch/video/10328419/bale-its-our-time-to-shine',
        'md5': '77d59166cddc8d3cb7b13e35eaf0f5ec',
        'info_dict': {
            'id': 'o3eWJnNDE6l7kfNO8BOoBlRxXRQ4ANNQ',
            'ext': 'mp4',
            'title': 'Bale: It\'s our time to shine',
            'description': 'md5:e88bda94ae15f7720c5cb467e777bb6d',
        },
        'add_ie': ['Ooyala'],
    }, {
        'url': 'https://www.skysports.com/watch/video/sports/f1/12160544/abu-dhabi-gp-the-notebook',
        'only_matching': True,
    }, {
        'url': 'https://www.skysports.com/watch/video/tv-shows/12118508/rainford-brent-how-ace-programme-helps',
        'only_matching': True,
    }]

class SkyNewsIE(SkyBaseIE):
    IE_NAME = 'sky:news'
    _VALID_URL = r'https?://news\.sky\.com/video/[0-9a-z-]+-(?P<id>[0-9]+)'
    _TEST = {
        'url': 'https://news.sky.com/video/russian-plane-inspected-after-deadly-fire-11712962',
        'md5': '411e8893fd216c75eaf7e4c65d364115',
        'info_dict': {
            'id': 'ref:1ua21xaDE6lCtZDmbYfl8kwsKLooJbNM',
            'ext': 'mp4',
            'title': 'Russian plane inspected after deadly fire',
            'description': 'The Russian Investigative Committee has released video of the wreckage of a passenger plane which caught fire near Moscow.',
            'uploader_id': '6058004172001',
            'timestamp': 1567112345,
            'upload_date': '20190829',
        },
        'add_ie': ['BrightcoveNew'],
    }

class SkyNewsStoryIE(SkyBaseIE):
    IE_NAME = 'sky:news:story'
    _VALID_URL = r'https?://news\.sky\.com/story/[0-9a-z-]+-(?P<id>[0-9]+)'
    _TEST = {
        'url': 'https://news.sky.com/story/budget-2021-chancellor-rishi-sunak-vows-address-will-deliver-strong-economy-fit-for-a-new-age-of-optimism-12445425',
        'info_dict': {
            'id': 'ref:0714acb9-123d-42c8-91b8-5c1bc6c73f20',
            'title': 'md5:e408dd7aad63f31a1817bbe40c7d276f',
            'description': 'md5:a881e12f49212f92be2befe4a09d288a',
            'ext': 'mp4',
            'upload_date': '20211027',
            'timestamp': 1635317494,
            'uploader_id': '6058004172001',
        }
    }

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

        entries = [self._process_ooyala_element(webpage, sdc_el, url)
                   for sdc_el in re.findall(self._SDC_EL_REGEX, webpage)]

        return self.playlist_result(
            entries, article_id, self._og_search_title(webpage),
            self._html_search_meta(['og:description', 'description'], webpage))

class SkySportsNewsIE(SkyBaseIE):
    IE_NAME = 'sky:sports:news'
    _VALID_URL = r'https?://(?:www\.)?skysports\.com/([^/]+/)*news/\d+/(?P<id>\d+)'
    _TEST = {
        'url': 'http://www.skysports.com/golf/news/12176/10871916/dustin-johnson-ready-to-conquer-players-championship-at-tpc-sawgrass',
        'info_dict': {
            'id': '10871916',
            'title': 'Dustin Johnson ready to conquer Players Championship at TPC Sawgrass',
            'description': 'Dustin Johnson is confident he can continue his dominant form in 2017 by adding the Players Championship to his list of victories.',
        },
        'playlist_count': 2,
    }

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

        entries = []
        for sdc_el in re.findall(self._SDC_EL_REGEX, webpage):
            entries.append(self._process_ooyala_element(webpage, sdc_el, url))

        return self.playlist_result(
            entries, article_id, self._og_search_title(webpage),
            self._html_search_meta(['og:description', 'description'], webpage))


code:
from .common import InfoExtractor
from ..compat import (
    compat_parse_qs,
    compat_urllib_parse_urlparse,
)
from ..utils import (
    dict_get,
    int_or_none,
    parse_duration,
    unified_timestamp,
)

class SkyItPlayerIE(InfoExtractor):
    IE_NAME = 'player.sky.it'
    _VALID_URL = r'https?://player\.sky\.it/player/(?:external|social)\.html\?.*?\bid=(?P<id>\d+)'
    _GEO_BYPASS = False
    _DOMAIN = 'sky'
    _PLAYER_TMPL = 'https://player.sky.it/player/external.html?id=%s&domain=%s'
    # http://static.sky.it/static/skyplayer/conf.json
    _TOKEN_MAP = {
        'cielo': 'Hh9O7M8ks5yi6nSROL7bKYz933rdf3GhwZlTLMgvy4Q',
        'hotclub': 'kW020K2jq2lk2eKRJD2vWEg832ncx2EivZlTLQput2C',
        'mtv8': 'A5Nn9GGb326CI7vP5e27d7E4PIaQjota',
        'salesforce': 'C6D585FD1615272C98DE38235F38BD86',
        'sitocommerciale': 'VJwfFuSGnLKnd9Phe9y96WkXgYDCguPMJ2dLhGMb2RE',
        'sky': 'F96WlOd8yoFmLQgiqv6fNQRvHZcsWk5jDaYnDvhbiJk',
        'skyacademy': 'A6LAn7EkO2Q26FRy0IAMBekX6jzDXYL3',
        'skyarte': 'LWk29hfiU39NNdq87ePeRach3nzTSV20o0lTv2001Cd',
        'theupfront': 'PRSGmDMsg6QMGc04Obpoy7Vsbn7i2Whp',
    }

    def _player_url_result(self, video_id):
        return self.url_result(
            self._PLAYER_TMPL % (video_id, self._DOMAIN),
            SkyItPlayerIE.ie_key(), video_id)

    def _parse_video(self, video, video_id):
        title = video['title']
        is_live = video.get('type') == 'live'
        hls_url = video.get(('streaming' if is_live else 'hls') + '_url')
        if not hls_url and video.get('geoblock' if is_live else 'geob'):
            self.raise_geo_restricted(countries=['IT'])

        if is_live:
            formats = self._extract_m3u8_formats(hls_url, video_id, 'mp4')
        else:
            formats = self._extract_akamai_formats(
                hls_url, video_id, {'http': 'videoplatform.sky.it'})
        self._sort_formats(formats)

        return {
            'id': video_id,
            'title': title,
            'formats': formats,
            'thumbnail': dict_get(video, ('video_still', 'video_still_medium', 'thumb')),
            'description': video.get('short_desc') or None,
            'timestamp': unified_timestamp(video.get('create_date')),
            'duration': int_or_none(video.get('duration_sec')) or parse_duration(video.get('duration')),
            'is_live': is_live,
        }

    def _real_extract(self, url):
        video_id = self._match_id(url)
        domain = compat_parse_qs(compat_urllib_parse_urlparse(
            url).query).get('domain', [None])[0]
        token = dict_get(self._TOKEN_MAP, (domain, 'sky'))
        video = self._download_json(
            'https://apid.sky.it/vdp/v1/getVideoData',
            video_id, query={
                'caller': 'sky',
                'id': video_id,
                'token': token
            }, headers=self.geo_verification_headers())
        return self._parse_video(video, video_id)

class SkyItVideoIE(SkyItPlayerIE):
    IE_NAME = 'video.sky.it'
    _VALID_URL = r'https?://(?:masterchef|video|xfactor)\.sky\.it(?:/[^/]+)*/video/[0-9a-z-]+-(?P<id>\d+)'
    _TESTS = [{
        'url': 'https://video.sky.it/news/mondo/video/uomo-ucciso-da-uno-squalo-in-australia-631227',
        'md5': 'fe5c91e59a84a3437eaa0bca6e134ccd',
        'info_dict': {
            'id': '631227',
            'ext': 'mp4',
            'title': 'Uomo ucciso da uno squalo in Australia',
            'timestamp': 1606036192,
            'upload_date': '20201122',
        }
    }, {
        'url': 'https://xfactor.sky.it/video/x-factor-2020-replay-audizioni-1-615820',
        'only_matching': True,
    }, {
        'url': 'https://masterchef.sky.it/video/masterchef-9-cosa-e-successo-nella-prima-puntata-562831',
        'only_matching': True,
    }]

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

class SkyItVideoLiveIE(SkyItPlayerIE):
    IE_NAME = 'video.sky.it:live'
    _VALID_URL = r'https?://video\.sky\.it/diretta/(?P<id>[^/?&#]+)'
    _TEST = {
        'url': 'https://video.sky.it/diretta/tg24',
        'info_dict': {
            'id': '1',
            'ext': 'mp4',
            'title': r're:Diretta TG24 \d{4}-\d{2}-\d{2} \d{2}:\d{2}',
            'description': 'Guarda la diretta streaming di SkyTg24, segui con Sky tutti gli appuntamenti e gli speciali di Tg24.',
        },
        'params': {
            # m3u8 download
            'skip_download': True,
        },
    }

    def _real_extract(self, url):
        display_id = self._match_id(url)
        webpage = self._download_webpage(url, display_id)
        asset_id = str(self._search_nextjs_data(webpage, display_id)['props']['initialState']['livePage']['content']['asset_id'])
        livestream = self._download_json(
            'https://apid.sky.it/vdp/v1/getLivestream',
            asset_id, query={'id': asset_id})
        return self._parse_video(livestream, asset_id)

class SkyItIE(SkyItPlayerIE):
    IE_NAME = 'sky.it'
    _VALID_URL = r'https?://(?:sport|tg24)\.sky\.it(?:/[^/]+)*/\d{4}/\d{2}/\d{2}/(?P<id>[^/?&#]+)'
    _TESTS = [{
        'url': 'https://sport.sky.it/calcio/serie-a/2020/11/21/juventus-cagliari-risultato-gol',
        'info_dict': {
            'id': '631201',
            'ext': 'mp4',
            'title': 'Un rosso alla violenza: in campo per i diritti delle donne',
            'upload_date': '20201121',
            'timestamp': 1605995753,
        },
        'expected_warnings': ['Unable to download f4m manifest'],
    }, {
        'url': 'https://tg24.sky.it/mondo/2020/11/22/australia-squalo-uccide-uomo',
        'md5': 'fe5c91e59a84a3437eaa0bca6e134ccd',
        'info_dict': {
            'id': '631227',
            'ext': 'mp4',
            'title': 'Uomo ucciso da uno squalo in Australia',
            'timestamp': 1606036192,
            'upload_date': '20201122',
        },
    }]
    _VIDEO_ID_REGEX = r'data-videoid="(\d+)"'

    def _real_extract(self, url):
        display_id = self._match_id(url)
        webpage = self._download_webpage(url, display_id)
        video_id = self._search_regex(
            self._VIDEO_ID_REGEX, webpage, 'video id')
        return self._player_url_result(video_id)

class SkyItAcademyIE(SkyItIE):
    IE_NAME = 'skyacademy.it'
    _VALID_URL = r'https?://(?:www\.)?skyacademy\.it(?:/[^/]+)*/\d{4}/\d{2}/\d{2}/(?P<id>[^/?&#]+)'
    _TESTS = [{
        'url': 'https://www.skyacademy.it/eventi-speciali/2019/07/05/a-lezione-di-cinema-con-sky-academy-/',
        'md5': 'ced5c26638b7863190cbc44dd6f6ba08',
        'info_dict': {
            'id': '523458',
            'ext': 'mp4',
            'title': 'Sky Academy "The Best CineCamp 2019"',
            'timestamp': 1562843784,
            'upload_date': '20190711',
        }
    }]
    _DOMAIN = 'skyacademy'
    _VIDEO_ID_REGEX = r'id="news-videoId_(\d+)"'

class SkyItArteIE(SkyItIE):
    IE_NAME = 'arte.sky.it'
    _VALID_URL = r'https?://arte\.sky\.it/video/(?P<id>[^/?&#]+)'
    _TESTS = [{
        'url': 'https://arte.sky.it/video/serie-musei-venezia-collezionismo-12-novembre/',
        'md5': '515aee97b87d7a018b6c80727d3e7e17',
        'info_dict': {
            'id': '627926',
            'ext': 'mp4',
            'title': "Musei Galleria Franchetti alla Ca' d'Oro Palazzo Grimani",
            'upload_date': '20201106',
            'timestamp': 1604664493,
        }
    }]
    _DOMAIN = 'skyarte'
    _VIDEO_ID_REGEX = r'(?s)<iframe[^>]+src="(?:https:)?//player\.sky\.it/player/external\.html\?[^"]*\bid=(\d+)'

class CieloTVItIE(SkyItIE):
    IE_NAME = 'cielotv.it'
    _VALID_URL = r'https?://(?:www\.)?cielotv\.it/video/(?P<id>[^.]+)\.html'
    _TESTS = [{
        'url': 'https://www.cielotv.it/video/Il-lunedi-e-sempre-un-dramma.html',
        'md5': 'c4deed77552ba901c2a0d9258320304b',
        'info_dict': {
            'id': '499240',
            'ext': 'mp4',
            'title': 'Il lunedì è sempre un dramma',
            'upload_date': '20190329',
            'timestamp': 1553862178,
        }
    }]
    _DOMAIN = 'cielo'
    _VIDEO_ID_REGEX = r'videoId\s*=\s*"(\d+)"'

class TV8ItIE(SkyItVideoIE):
    IE_NAME = 'tv8.it'
    _VALID_URL = r'https?://tv8\.it/showvideo/(?P<id>\d+)'
    _TESTS = [{
        'url': 'https://tv8.it/showvideo/630529/ogni-mattina-ucciso-asino-di-andrea-lo-cicero/18-11-2020/',
        'md5': '9ab906a3f75ea342ed928442f9dabd21',
        'info_dict': {
            'id': '630529',
            'ext': 'mp4',
            'title': 'Ogni mattina - Ucciso asino di Andrea Lo Cicero',
            'timestamp': 1605721374,
            'upload_date': '20201118',
        }
    }]
    _DOMAIN = 'mtv8'


code:
from .common import InfoExtractor

class SkylineWebcamsIE(InfoExtractor):
    _VALID_URL = r'https?://(?:www\.)?skylinewebcams\.com/[^/]+/webcam/(?:[^/]+/)+(?P<id>[^/]+)\.html'
    _TEST = {
        'url': 'https://www.skylinewebcams.com/it/webcam/italia/lazio/roma/scalinata-piazza-di-spagna-barcaccia.html',
        'info_dict': {
            'id': 'scalinata-piazza-di-spagna-barcaccia',
            'ext': 'mp4',
            'title': 're:^Live Webcam Scalinata di Piazza di Spagna - La Barcaccia [0-9]{4}-[0-9]{2}-[0-9]{2} [0-9]{2}:[0-9]{2}$',
            'description': 'Roma, veduta sulla Scalinata di Piazza di Spagna e sulla Barcaccia',
            'is_live': True,
        },
        'params': {
            'skip_download': True,
        }
    }

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

        webpage = self._download_webpage(url, video_id)

        stream_url = self._search_regex(
            r'(?:url|source)\s*:\s*(["\'])(?P<url>(?:https?:)?//.+?\.m3u8.*?)\1', webpage,
            'stream url', group='url')

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

        return {
            'id': video_id,
            'url': stream_url,
            'ext': 'mp4',
            'title': title,
            'description': description,
            'is_live': True,
        }


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

class SkyNewsArabiaBaseIE(InfoExtractor):
    _IMAGE_BASE_URL = 'http://www.skynewsarabia.com/web/images'

    def _call_api(self, path, value):
        return self._download_json('http://api.skynewsarabia.com/web/rest/v2/%s/%s.json' % (path, value), value)

    def _get_limelight_media_id(self, url):
        return self._search_regex(r'/media/[^/]+/([a-z0-9]{32})', url, 'limelight media id')

    def _get_image_url(self, image_path_template, width='1600', height='1200'):
        return self._IMAGE_BASE_URL + image_path_template.format(width=width, height=height)

    def _extract_video_info(self, video_data):
        video_id = compat_str(video_data['id'])
        topic = video_data.get('topicTitle')
        return {
            '_type': 'url_transparent',
            'url': 'limelight:media:%s' % self._get_limelight_media_id(video_data['videoUrl'][0]['url']),
            'id': video_id,
            'title': video_data['headline'],
            'description': video_data.get('summary'),
            'thumbnail': self._get_image_url(video_data['mediaAsset']['imageUrl']),
            'timestamp': parse_iso8601(video_data.get('date')),
            'duration': parse_duration(video_data.get('runTime')),
            'tags': video_data.get('tags', []),
            'categories': [topic] if topic else [],
            'webpage_url': 'http://www.skynewsarabia.com/web/video/%s' % video_id,
            'ie_key': 'LimelightMedia',
        }

class SkyNewsArabiaIE(SkyNewsArabiaBaseIE):
    IE_NAME = 'skynewsarabia:video'
    _VALID_URL = r'https?://(?:www\.)?skynewsarabia\.com/web/video/(?P<id>[0-9]+)'
    _TEST = {
        'url': 'http://www.skynewsarabia.com/web/video/794902/%D9%86%D8%B5%D9%81-%D9%85%D9%84%D9%8A%D9%88%D9%86-%D9%85%D8%B5%D8%A8%D8%A7%D8%AD-%D8%B4%D8%AC%D8%B1%D8%A9-%D9%83%D8%B1%D9%8A%D8%B3%D9%85%D8%A7%D8%B3',
        'info_dict': {
            'id': '794902',
            'ext': 'flv',
            'title': 'نصف مليون مصباح على شجرة كريسماس',
            'description': 'md5:22f1b27f0850eeb10c7e59b1f16eb7c6',
            'upload_date': '20151128',
            'timestamp': 1448697198,
            'duration': 2119,
        },
        'params': {
            # rtmp download
            'skip_download': True,
        },
    }

    def _real_extract(self, url):
        video_id = self._match_id(url)
        video_data = self._call_api('video', video_id)
        return self._extract_video_info(video_data)

class SkyNewsArabiaArticleIE(SkyNewsArabiaBaseIE):
    IE_NAME = 'skynewsarabia:article'
    _VALID_URL = r'https?://(?:www\.)?skynewsarabia\.com/web/article/(?P<id>[0-9]+)'
    _TESTS = [{
        'url': 'http://www.skynewsarabia.com/web/article/794549/%D8%A7%D9%94%D8%AD%D8%AF%D8%A7%D8%AB-%D8%A7%D9%84%D8%B4%D8%B1%D9%82-%D8%A7%D9%84%D8%A7%D9%94%D9%88%D8%B3%D8%B7-%D8%AE%D8%B1%D9%8A%D8%B7%D8%A9-%D8%A7%D9%84%D8%A7%D9%94%D9%84%D8%B9%D8%A7%D8%A8-%D8%A7%D9%84%D8%B0%D9%83%D9%8A%D8%A9',
        'info_dict': {
            'id': '794549',
            'ext': 'flv',
            'title': 'بالفيديو.. ألعاب ذكية تحاكي واقع المنطقة',
            'description': 'md5:0c373d29919a851e080ee4edd0c5d97f',
            'upload_date': '20151126',
            'timestamp': 1448559336,
            'duration': 281.6,
        },
        'params': {
            # rtmp download
            'skip_download': True,
        },
    }, {
        'url': 'http://www.skynewsarabia.com/web/article/794844/%D8%A7%D8%B3%D8%AA%D9%87%D8%AF%D8%A7%D9%81-%D9%82%D9%88%D8%A7%D8%B1%D8%A8-%D8%A7%D9%94%D8%B3%D9%84%D8%AD%D8%A9-%D9%84%D9%85%D9%8A%D9%84%D9%8A%D8%B4%D9%8A%D8%A7%D8%AA-%D8%A7%D9%84%D8%AD%D9%88%D8%AB%D9%8A-%D9%88%D8%B5%D8%A7%D9%84%D8%AD',
        'info_dict': {
            'id': '794844',
            'title': 'إحباط تهريب أسلحة لميليشيات الحوثي وصالح بجنوب اليمن',
            'description': 'md5:5c927b8b2e805796e7f693538d96fc7e',
        },
        'playlist_mincount': 2,
    }]

    def _real_extract(self, url):
        article_id = self._match_id(url)
        article_data = self._call_api('article', article_id)
        media_asset = article_data['mediaAsset']
        if media_asset['type'] == 'VIDEO':
            topic = article_data.get('topicTitle')
            return {
                '_type': 'url_transparent',
                'url': 'limelight:media:%s' % self._get_limelight_media_id(media_asset['videoUrl'][0]['url']),
                'id': article_id,
                'title': article_data['headline'],
                'description': article_data.get('summary'),
                'thumbnail': self._get_image_url(media_asset['imageUrl']),
                'timestamp': parse_iso8601(article_data.get('date')),
                'tags': article_data.get('tags', []),
                'categories': [topic] if topic else [],
                'webpage_url': url,
                'ie_key': 'LimelightMedia',
            }
        entries = [self._extract_video_info(item) for item in article_data.get('inlineItems', []) if item['type'] == 'VIDEO']
        return self.playlist_result(entries, article_id, article_data['headline'], article_data.get('summary'))


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

class SkyNewsAUIE(InfoExtractor):
    _VALID_URL = r'https?://(?:www\.)?skynews\.com\.au/[^/]+/[^/]+/[^/]+/video/(?P<id>[a-z0-9]+)'

    _TESTS = [{
        'url': 'https://www.skynews.com.au/world-news/united-states/incredible-vision-shows-lava-overflowing-from-spains-la-palma-volcano/video/0f4c6243d6903502c01251f228b91a71',
        'info_dict': {
            'id': '6277184925001',
            'ext': 'mp4',
            'title': 'md5:60594f1ea6d5ae93e292900f4d34e9ae',
            'description': 'md5:60594f1ea6d5ae93e292900f4d34e9ae',
            'thumbnail': r're:^https?://.*\.jpg',
            'duration': 76.394,
            'timestamp': 1634271300,
            'uploader_id': '5348771529001',
            'tags': ['fblink', 'msn', 'usa', 'world', 'yt'],
            'upload_date': '20211015',
        },
        'params': {'skip_download': True, 'format': 'bv'}
    }]

    _API_KEY = '6krsj3w249nk779d8fukqx9f'

    def _real_extract(self, url):
        id = self._match_id(url)
        webpage = self._download_webpage(url, id)
        embedcode = self._search_regex(r'embedcode\s?=\s?\"([^\"]+)\"', webpage, 'embedcode')
        data_json = self._download_json(
            f'https://content.api.news/v3/videos/brightcove/{embedcode}?api_key={self._API_KEY}', id)['content']
        return {
            'id': id,
            '_type': 'url_transparent',
            'url': 'https://players.brightcove.net/%s/default_default/index.html?videoId=%s' % tuple(embedcode.split('-')),
            'ie_key': 'BrightcoveNew',
            'title': data_json.get('caption'),
            'upload_date': unified_strdate(try_get(data_json, lambda x: x['date']['created'])),
        }


code:
import json

from .common import InfoExtractor
from ..compat import (
    compat_urlparse,
)
from ..utils import (
    ExtractorError,
    get_element_by_id,
)

class SlideshareIE(InfoExtractor):
    _VALID_URL = r'https?://(?:www\.)?slideshare\.net/[^/]+?/(?P<title>.+?)($|\?)'

    _TEST = {
        'url': 'http://www.slideshare.net/Dataversity/keynote-presentation-managing-scale-and-complexity',
        'info_dict': {
            'id': '25665706',
            'ext': 'mp4',
            'title': 'Managing Scale and Complexity',
            'description': 'This was a keynote presentation at the NoSQL Now! 2013 Conference & Expo (http://www.nosqlnow.com). This presentation was given by Adrian Cockcroft from Netflix.',
        },
    }

    def _real_extract(self, url):
        mobj = self._match_valid_url(url)
        page_title = mobj.group('title')
        webpage = self._download_webpage(url, page_title)
        slideshare_obj = self._search_regex(
            r'\$\.extend\(.*?slideshare_object,\s*(\{.*?\})\);',
            webpage, 'slideshare object')
        info = json.loads(slideshare_obj)
        if info['slideshow']['type'] != 'video':
            raise ExtractorError('Webpage type is "%s": only video extraction is supported for Slideshare' % info['slideshow']['type'], expected=True)

        doc = info['doc']
        bucket = info['jsplayer']['video_bucket']
        ext = info['jsplayer']['video_extension']
        video_url = compat_urlparse.urljoin(bucket, doc + '-SD.' + ext)
        description = get_element_by_id('slideshow-description-paragraph', webpage) or self._html_search_regex(
            r'(?s)<p[^>]+itemprop="description"[^>]*>(.+?)</p>', webpage,
            'description', fatal=False)

        return {
            '_type': 'video',
            'id': info['slideshow']['id'],
            'title': info['slideshow']['title'],
            'ext': ext,
            'url': video_url,
            'thumbnail': info['slideshow']['pin_image_url'],
            'description': description.strip() if description else None,
        }


code:
from .common import InfoExtractor
from ..utils import (
    bool_or_none,
    smuggle_url,
    try_get,
    url_or_none,
)

class SlidesLiveIE(InfoExtractor):
    _VALID_URL = r'https?://slideslive\.com/(?P<id>[0-9]+)'
    _TESTS = [{
        # video_service_name = YOUTUBE
        'url': 'https://slideslive.com/38902413/gcc-ia16-backend',
        'md5': 'b29fcd6c6952d0c79c5079b0e7a07e6f',
        'info_dict': {
            'id': 'LMtgR8ba0b0',
            'ext': 'mp4',
            'title': 'GCC IA16 backend',
            'description': 'Watch full version of this video at https://slideslive.com/38902413.',
            'uploader': 'SlidesLive Videos - A',
            'uploader_id': 'UC62SdArr41t_-_fX40QCLRw',
            'timestamp': 1597615266,
            'upload_date': '20170925',
        }
    }, {
        # video_service_name = yoda
        'url': 'https://slideslive.com/38935785',
        'md5': '575cd7a6c0acc6e28422fe76dd4bcb1a',
        'info_dict': {
            'id': 'RMraDYN5ozA_',
            'ext': 'mp4',
            'title': 'Offline Reinforcement Learning: From Algorithms to Practical Challenges',
        },
    }, {
        # video_service_name = youtube
        'url': 'https://slideslive.com/38903721/magic-a-scientific-resurrection-of-an-esoteric-legend',
        'only_matching': True,
    }, {
        # video_service_name = url
        'url': 'https://slideslive.com/38922070/learning-transferable-skills-1',
        'only_matching': True,
    }, {
        # video_service_name = vimeo
        'url': 'https://slideslive.com/38921896/retrospectives-a-venue-for-selfreflection-in-ml-research-3',
        'only_matching': True,
    }]

    def _real_extract(self, url):
        video_id = self._match_id(url)
        video_data = self._download_json(
            'https://ben.slideslive.com/player/' + video_id, video_id)
        service_name = video_data['video_service_name'].lower()
        assert service_name in ('url', 'yoda', 'vimeo', 'youtube')
        service_id = video_data['video_service_id']
        subtitles = {}
        for sub in try_get(video_data, lambda x: x['subtitles'], list) or []:
            if not isinstance(sub, dict):
                continue
            webvtt_url = url_or_none(sub.get('webvtt_url'))
            if not webvtt_url:
                continue
            lang = sub.get('language') or 'en'
            subtitles.setdefault(lang, []).append({
                'url': webvtt_url,
            })
        info = {
            'id': video_id,
            'thumbnail': video_data.get('thumbnail'),
            'is_live': bool_or_none(video_data.get('is_live')),
            'subtitles': subtitles,
        }
        if service_name in ('url', 'yoda'):
            info['title'] = video_data['title']
            if service_name == 'url':
                info['url'] = service_id
            else:
                formats = []
                _MANIFEST_PATTERN = 'https://01.cdn.yoda.slideslive.com/%s/master.%s'
                # use `m3u8` entry_protocol until EXT-X-MAP is properly supported by `m3u8_native` entry_protocol
                formats.extend(self._extract_m3u8_formats(
                    _MANIFEST_PATTERN % (service_id, 'm3u8'),
                    service_id, 'mp4', m3u8_id='hls', fatal=False))
                formats.extend(self._extract_mpd_formats(
                    _MANIFEST_PATTERN % (service_id, 'mpd'), service_id,
                    mpd_id='dash', fatal=False))
                self._sort_formats(formats)
                info.update({
                    'id': service_id,
                    'formats': formats,
                })
        else:
            info.update({
                '_type': 'url_transparent',
                'url': service_id,
                'ie_key': service_name.capitalize(),
                'title': video_data.get('title'),
            })
            if service_name == 'vimeo':
                info['url'] = smuggle_url(
                    'https://player.vimeo.com/video/' + service_id,
                    {'http_headers': {'Referer': url}})
        return info


code:
from .common import InfoExtractor

class SlutloadIE(InfoExtractor):
    _VALID_URL = r'https?://(?:\w+\.)?slutload\.com/(?:video/[^/]+|embed_player|watch)/(?P<id>[^/]+)'
    _TESTS = [{
        'url': 'http://www.slutload.com/video/virginie-baisee-en-cam/TD73btpBqSxc/',
        'md5': '868309628ba00fd488cf516a113fd717',
        'info_dict': {
            'id': 'TD73btpBqSxc',
            'ext': 'mp4',
            'title': 'virginie baisee en cam',
            'age_limit': 18,
            'thumbnail': r're:https?://.*?\.jpg'
        },
    }, {
        # mobile site
        'url': 'http://mobile.slutload.com/video/masturbation-solo/fviFLmc6kzJ/',
        'only_matching': True,
    }, {
        'url': 'http://www.slutload.com/embed_player/TD73btpBqSxc/',
        'only_matching': True,
    }, {
        'url': 'http://www.slutload.com/watch/TD73btpBqSxc/Virginie-Baisee-En-Cam.html',
        'only_matching': True,
    }]

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

        embed_page = self._download_webpage(
            'http://www.slutload.com/embed_player/%s' % video_id, video_id,
            'Downloading embed page', fatal=False)

        if embed_page:
            def extract(what):
                return self._html_search_regex(
                    r'data-video-%s=(["\'])(?P<url>(?:(?!\1).)+)\1' % what,
                    embed_page, 'video %s' % what, default=None, group='url')

            video_url = extract('url')
            if video_url:
                title = self._html_search_regex(
                    r'<title>([^<]+)', embed_page, 'title', default=video_id)
                return {
                    'id': video_id,
                    'url': video_url,
                    'title': title,
                    'thumbnail': extract('preview'),
                    'age_limit': 18
                }

        webpage = self._download_webpage(
            'http://www.slutload.com/video/_/%s/' % video_id, video_id)
        title = self._html_search_regex(
            r'
([^<]+)', webpage, 'title').strip()
        info = self._parse_html5_media_entries(url, webpage, video_id)[0]
        info.update({
            'id': video_id,
            'title': title,
            'age_limit': 18,
        })
        return info


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

class SnotrIE(InfoExtractor):
    _VALID_URL = r'http?://(?:www\.)?snotr\.com/video/(?P<id>\d+)/([\w]+)'
    _TESTS = [{
        'url': 'http://www.snotr.com/video/13708/Drone_flying_through_fireworks',
        'info_dict': {
            'id': '13708',
            'ext': 'mp4',
            'title': 'Drone flying through fireworks!',
            'duration': 248,
            'filesize_approx': 40700000,
            'description': 'A drone flying through Fourth of July Fireworks',
            'thumbnail': r're:^https?://.*\.jpg$',
        },
        'expected_warnings': ['description'],
    }, {
        'url': 'http://www.snotr.com/video/530/David_Letteman_-_George_W_Bush_Top_10',
        'info_dict': {
            'id': '530',
            'ext': 'mp4',
            'title': 'David Letteman - George W. Bush Top 10',
            'duration': 126,
            'filesize_approx': 8500000,
            'description': 'The top 10 George W. Bush moments, brought to you by David Letterman!',
            'thumbnail': r're:^https?://.*\.jpg$',
        }
    }]

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

        webpage = self._download_webpage(url, video_id)
        title = self._og_search_title(webpage)

        description = self._og_search_description(webpage)
        info_dict = self._parse_html5_media_entries(
            url, webpage, video_id, m3u8_entry_protocol='m3u8_native')[0]

        view_count = str_to_int(self._html_search_regex(
            r'
]*>\s*]*>Views:\s*]*>([\d,\.]+)',
            webpage, 'view count', fatal=False))

        duration = parse_duration(self._html_search_regex(
            r'
]*>\s*]*>Length:\s*]*>([\d:]+)',
            webpage, 'duration', fatal=False))

        filesize_approx = parse_filesize(self._html_search_regex(
            r'
]*>\s*]*>Filesize:\s*]*>([^<]+)',
            webpage, 'filesize', fatal=False))

        info_dict.update({
            'id': video_id,
            'description': description,
            'title': title,
            'view_count': view_count,
            'duration': duration,
            'filesize_approx': filesize_approx,
        })

        return info_dict


code:
import re

from .common import InfoExtractor
from ..compat import (
    compat_str,
    compat_urllib_parse_urlencode,
)
from ..utils import (
    ExtractorError,
    int_or_none,
    try_get,
)

class SohuIE(InfoExtractor):
    _VALID_URL = r'https?://(?P<mytv>my\.)?tv\.sohu\.com/.+?/(?(mytv)|n)(?P<id>\d+)\.shtml.*?'

    # Sohu videos give different MD5 sums on Travis CI and my machine
    _TESTS = [{
        'note': 'This video is available only in Mainland China',
        'url': 'http://tv.sohu.com/20130724/n382479172.shtml#super',
        'info_dict': {
            'id': '382479172',
            'ext': 'mp4',
            'title': 'MV：Far East Movement《The Illest》',
        },
        'skip': 'On available in China',
    }, {
        'url': 'http://tv.sohu.com/20150305/n409385080.shtml',
        'info_dict': {
            'id': '409385080',
            'ext': 'mp4',
            'title': '《2015湖南卫视羊年元宵晚会》唐嫣《花好月圆》',
        }
    }, {
        'url': 'http://my.tv.sohu.com/us/232799889/78693464.shtml',
        'info_dict': {
            'id': '78693464',
            'ext': 'mp4',
            'title': '【爱范品】第31期：MWC见不到的奇葩手机',
        }
    }, {
        'note': 'Multipart video',
        'url': 'http://my.tv.sohu.com/pl/8384802/78910339.shtml',
        'info_dict': {
            'id': '78910339',
            'title': '【神探苍实战秘籍】第13期 战争之影 赫卡里姆',
        },
        'playlist': [{
            'info_dict': {
                'id': '78910339_part1',
                'ext': 'mp4',
                'duration': 294,
                'title': '【神探苍实战秘籍】第13期 战争之影 赫卡里姆',
            }
        }, {
            'info_dict': {
                'id': '78910339_part2',
                'ext': 'mp4',
                'duration': 300,
                'title': '【神探苍实战秘籍】第13期 战争之影 赫卡里姆',
            }
        }, {
            'info_dict': {
                'id': '78910339_part3',
                'ext': 'mp4',
                'duration': 150,
                'title': '【神探苍实战秘籍】第13期 战争之影 赫卡里姆',
            }
        }]
    }, {
        'note': 'Video with title containing dash',
        'url': 'http://my.tv.sohu.com/us/249884221/78932792.shtml',
        'info_dict': {
            'id': '78932792',
            'ext': 'mp4',
            'title': 'youtube-dl testing video',
        },
        'params': {
            'skip_download': True
        }
    }]

    def _real_extract(self, url):

        def _fetch_data(vid_id, mytv=False):
            if mytv:
                base_data_url = 'http://my.tv.sohu.com/play/videonew.do?vid='
            else:
                base_data_url = 'http://hot.vrs.sohu.com/vrs_flash.action?vid='

            return self._download_json(
                base_data_url + vid_id, video_id,
                'Downloading JSON data for %s' % vid_id,
                headers=self.geo_verification_headers())

        mobj = self._match_valid_url(url)
        video_id = mobj.group('id')
        mytv = mobj.group('mytv') is not None

        webpage = self._download_webpage(url, video_id)

        title = re.sub(r' - 搜狐视频$', '', self._og_search_title(webpage))

        vid = self._html_search_regex(
            r'var vid ?= ?["\'](\d+)["\']',
            webpage, 'video path')
        vid_data = _fetch_data(vid, mytv)
        if vid_data['play'] != 1:
            if vid_data.get('status') == 12:
                raise ExtractorError(
                    '%s said: There\'s something wrong in the video.' % self.IE_NAME,
                    expected=True)
            else:
                self.raise_geo_restricted(
                    '%s said: The video is only licensed to users in Mainland China.' % self.IE_NAME)

        formats_json = {}
        for format_id in ('nor', 'high', 'super', 'ori', 'h2644k', 'h2654k'):
            vid_id = vid_data['data'].get('%sVid' % format_id)
            if not vid_id:
                continue
            vid_id = compat_str(vid_id)
            formats_json[format_id] = vid_data if vid == vid_id else _fetch_data(vid_id, mytv)

        part_count = vid_data['data']['totalBlocks']

        playlist = []
        for i in range(part_count):
            formats = []
            for format_id, format_data in formats_json.items():
                allot = format_data['allot']

                data = format_data['data']
                clips_url = data['clipsURL']
                su = data['su']

                video_url = 'newflv.sohu.ccgslb.net'
                cdnId = None
                retries = 0

                while 'newflv.sohu.ccgslb.net' in video_url:
                    params = {
                        'prot': 9,
                        'file': clips_url[i],
                        'new': su[i],
                        'prod': 'flash',
                        'rb': 1,
                    }

                    if cdnId is not None:
                        params['idc'] = cdnId

                    download_note = 'Downloading %s video URL part %d of %d' % (
                        format_id, i + 1, part_count)

                    if retries > 0:
                        download_note += ' (retry #%d)' % retries
                    part_info = self._parse_json(self._download_webpage(
                        'http://%s/?%s' % (allot, compat_urllib_parse_urlencode(params)),
                        video_id, download_note), video_id)

                    video_url = part_info['url']
                    cdnId = part_info.get('nid')

                    retries += 1
                    if retries > 5:
                        raise ExtractorError('Failed to get video URL')

                formats.append({
                    'url': video_url,
                    'format_id': format_id,
                    'filesize': int_or_none(
                        try_get(data, lambda x: x['clipsBytes'][i])),
                    'width': int_or_none(data.get('width')),
                    'height': int_or_none(data.get('height')),
                    'fps': int_or_none(data.get('fps')),
                })
            self._sort_formats(formats)

            playlist.append({
                'id': '%s_part%d' % (video_id, i + 1),
                'title': title,
                'duration': vid_data['data']['clipsDuration'][i],
                'formats': formats,
            })

        if len(playlist) == 1:
            info = playlist[0]
            info['id'] = video_id
        else:
            info = {
                '_type': 'multi_video',
                'entries': playlist,
                'id': video_id,
                'title': title,
            }

        return info


code:
import datetime
import json
import math
import random
import time
import uuid

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

class SonyLIVIE(InfoExtractor):
    _VALID_URL = r'''(?x)
                     (?:
                        sonyliv:|
                        https?://(?:www\.)?sonyliv\.com/(?:s(?:how|port)s/[^/]+|movies|clip|trailer|music-videos)/[^/?#&]+-
                    )
                    (?P<id>\d+)
                  '''
    _TESTS = [{
        'url': 'https://www.sonyliv.com/shows/bachelors-delight-1700000113/achaari-cheese-toast-1000022678?watch=true',
        'info_dict': {
            'title': 'Achaari Cheese Toast',
            'id': '1000022678',
            'ext': 'mp4',
            'upload_date': '20200411',
            'description': 'md5:3957fa31d9309bf336ceb3f37ad5b7cb',
            'timestamp': 1586632091,
            'duration': 185,
            'season_number': 1,
            'series': 'Bachelors Delight',
            'episode_number': 1,
            'release_year': 2016,
        },
        'params': {
            'skip_download': True,
        },
    }, {
        'url': 'https://www.sonyliv.com/movies/tahalka-1000050121?watch=true',
        'only_matching': True,
    }, {
        'url': 'https://www.sonyliv.com/clip/jigarbaaz-1000098925',
        'only_matching': True,
    }, {
        'url': 'https://www.sonyliv.com/trailer/sandwiched-forever-1000100286?watch=true',
        'only_matching': True,
    }, {
        'url': 'https://www.sonyliv.com/sports/india-tour-of-australia-2020-21-1700000286/cricket-hls-day-3-1st-test-aus-vs-ind-19-dec-2020-1000100959?watch=true',
        'only_matching': True,
    }, {
        'url': 'https://www.sonyliv.com/music-videos/yeh-un-dinon-ki-baat-hai-1000018779',
        'only_matching': True,
    }]
    _GEO_COUNTRIES = ['IN']
    _HEADERS = {}
    _LOGIN_HINT = 'Use "--username <mobile_number>" to login using OTP or "--username token --password <auth_token>" to login using auth token.'
    _NETRC_MACHINE = 'sonyliv'

    def _get_device_id(self):
        e = int(time.time() * 1000)
        t = list('xxxxxxxxxxxx4xxxyxxxxxxxxxxxxxxx')
        for i, c in enumerate(t):
            n = int((e + 16 * random.random()) % 16) | 0
            e = math.floor(e / 16)
            if c == 'x':
                t[i] = str(n)
            elif c == 'y':
                t[i] = '{:x}'.format(3 & n | 8)
        return ''.join(t) + '-' + str(int(time.time() * 1000))

    def _perform_login(self, username, password):
        self._HEADERS['device_id'] = self._get_device_id()
        self._HEADERS['content-type'] = 'application/json'

        if username.lower() == 'token' and len(password) > 1198:
            self._HEADERS['authorization'] = password
        elif len(username) != 10 or not username.isdigit():
            raise ExtractorError(f'Invalid username/password; {self._LOGIN_HINT}')

        self.report_login()
        otp_request_json = self._download_json(
            'https://apiv2.sonyliv.com/AGL/1.6/A/ENG/WEB/IN/HR/CREATEOTP-V2',
            None, note='Sending OTP', headers=self._HEADERS, data=json.dumps({
                'mobileNumber': username,
                'channelPartnerID': 'MSMIND',
                'country': 'IN',
                'timestamp': datetime.datetime.now().strftime('%Y-%m-%dT%H:%M:%S.%MZ'),
                'otpSize': 6,
                'loginType': 'REGISTERORSIGNIN',
                'isMobileMandatory': True,
            }).encode())
        if otp_request_json['resultCode'] == 'KO':
            raise ExtractorError(otp_request_json['message'], expected=True)

        otp_verify_json = self._download_json(
            'https://apiv2.sonyliv.com/AGL/2.0/A/ENG/WEB/IN/HR/CONFIRMOTP-V2',
            None, note='Verifying OTP', headers=self._HEADERS, data=json.dumps({
                'channelPartnerID': 'MSMIND',
                'mobileNumber': username,
                'country': 'IN',
                'otp': self._get_tfa_info('OTP'),
                'dmaId': 'IN',
                'ageConfirmation': True,
                'timestamp': datetime.datetime.now().strftime('%Y-%m-%dT%H:%M:%S.%MZ'),
                'isMobileMandatory': True,
            }).encode())
        if otp_verify_json['resultCode'] == 'KO':
            raise ExtractorError(otp_request_json['message'], expected=True)
        self._HEADERS['authorization'] = otp_verify_json['resultObj']['accessToken']

    def _call_api(self, version, path, video_id):
        try:
            return self._download_json(
                'https://apiv2.sonyliv.com/AGL/%s/A/ENG/WEB/%s' % (version, path),
                video_id, headers=self._HEADERS)['resultObj']
        except ExtractorError as e:
            if isinstance(e.cause, compat_HTTPError) and e.cause.code == 406 and self._parse_json(
                    e.cause.read().decode(), video_id)['message'] == 'Please subscribe to watch this content':
                self.raise_login_required(self._LOGIN_HINT, method=None)
            if isinstance(e.cause, compat_HTTPError) and e.cause.code == 403:
                message = self._parse_json(
                    e.cause.read().decode(), video_id)['message']
                if message == 'Geoblocked Country':
                    self.raise_geo_restricted(countries=self._GEO_COUNTRIES)
                raise ExtractorError(message)
            raise

    def _initialize_pre_login(self):
        self._HEADERS['security_token'] = self._call_api('1.4', 'ALL/GETTOKEN', None)

    def _real_extract(self, url):
        video_id = self._match_id(url)
        content = self._call_api(
            '1.5', 'IN/CONTENT/VIDEOURL/VOD/' + video_id, video_id)
        if not self.get_param('allow_unplayable_formats') and content.get('isEncrypted'):
            self.report_drm(video_id)
        dash_url = content['videoURL']
        headers = {
            'x-playback-session-id': '%s-%d' % (uuid.uuid4().hex, time.time() * 1000)
        }
        formats = self._extract_mpd_formats(
            dash_url, video_id, mpd_id='dash', headers=headers, fatal=False)
        formats.extend(self._extract_m3u8_formats(
            dash_url.replace('.mpd', '.m3u8').replace('/DASH/', '/HLS/'),
            video_id, 'mp4', m3u8_id='hls', headers=headers, fatal=False))
        for f in formats:
            f.setdefault('http_headers', {}).update(headers)
        self._sort_formats(formats)

        metadata = self._call_api(
            '1.6', 'IN/DETAIL/' + video_id, video_id)['containers'][0]['metadata']
        title = metadata['episodeTitle']
        subtitles = {}
        for sub in content.get('subtitle', []):
            sub_url = sub.get('subtitleUrl')
            if not sub_url:
                continue
            subtitles.setdefault(sub.get('subtitleLanguageName', 'ENG'), []).append({
                'url': sub_url,
            })
        return {
            'id': video_id,
            'title': title,
            'formats': formats,
            'thumbnail': content.get('posterURL'),
            'description': metadata.get('longDescription') or metadata.get('shortDescription'),
            'timestamp': int_or_none(metadata.get('creationDate'), 1000),
            'duration': int_or_none(metadata.get('duration')),
            'season_number': int_or_none(metadata.get('season')),
            'series': metadata.get('title'),
            'episode_number': int_or_none(metadata.get('episodeNumber')),
            'release_year': int_or_none(metadata.get('year')),
            'subtitles': subtitles,
        }

class SonyLIVSeriesIE(InfoExtractor):
    _VALID_URL = r'https?://(?:www\.)?sonyliv\.com/shows/[^/?#&]+-(?P<id>\d{10})$'
    _TESTS = [{
        'url': 'https://www.sonyliv.com/shows/adaalat-1700000091',
        'playlist_mincount': 456,
        'info_dict': {
            'id': '1700000091',
        },
    }]
    _API_SHOW_URL = "https://apiv2.sonyliv.com/AGL/1.9/R/ENG/WEB/IN/DL/DETAIL/{}?kids_safe=false&from=0&to=49"
    _API_EPISODES_URL = "https://apiv2.sonyliv.com/AGL/1.4/R/ENG/WEB/IN/CONTENT/DETAIL/BUNDLE/{}?from=0&to=1000&orderBy=episodeNumber&sortOrder=asc"
    _API_SECURITY_URL = 'https://apiv2.sonyliv.com/AGL/1.4/A/ENG/WEB/ALL/GETTOKEN'

    def _entries(self, show_id):
        headers = {
            'Accept': 'application/json, text/plain, */*',
            'Referer': 'https://www.sonyliv.com',
        }
        headers['security_token'] = self._download_json(
            self._API_SECURITY_URL, video_id=show_id, headers=headers,
            note='Downloading security token')['resultObj']
        seasons = try_get(
            self._download_json(self._API_SHOW_URL.format(show_id), video_id=show_id, headers=headers),
            lambda x: x['resultObj']['containers'][0]['containers'], list)
        for season in seasons or []:
            season_id = season['id']
            episodes = try_get(
                self._download_json(self._API_EPISODES_URL.format(season_id), video_id=season_id, headers=headers),
                lambda x: x['resultObj']['containers'][0]['containers'], list)
            for episode in episodes or []:
                video_id = episode.get('id')
                yield self.url_result('sonyliv:%s' % video_id, ie=SonyLIVIE.ie_key(), video_id=video_id)

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