code:
from __future__ import unicode_literals

import re

from .common import InfoExtractor
from ..utils import (
    int_or_none,
    unified_strdate,
    xpath_text,
    determine_ext,
    float_or_none,
    ExtractorError,
)

class DreiSatIE(InfoExtractor):
    IE_NAME = '3sat'
    _GEO_COUNTRIES = ['DE']
    _VALID_URL = r'https?://(?:www\.)?3sat\.de/mediathek/(?:(?:index|mediathek)\.php)?\?(?:(?:mode|display)=[^&]+&)*obj=(?P<id>[0-9]+)'
    _TESTS = [
        {
            'url': 'http://www.3sat.de/mediathek/index.php?mode=play&obj=45918',
            'md5': 'be37228896d30a88f315b638900a026e',
            'info_dict': {
                'id': '45918',
                'ext': 'mp4',
                'title': 'Waidmannsheil',
                'description': 'md5:cce00ca1d70e21425e72c86a98a56817',
                'uploader': 'SCHWEIZWEIT',
                'uploader_id': '100000210',
                'upload_date': '20140913'
            },
            'params': {
                'skip_download': True,  # m3u8 downloads
            }
        },
        {
            'url': 'http://www.3sat.de/mediathek/mediathek.php?mode=play&obj=51066',
            'only_matching': True,
        },
    ]

    def _parse_smil_formats(self, smil, smil_url, video_id, namespace=None, f4m_params=None, transform_rtmp_url=None):
        param_groups = {}
        for param_group in smil.findall(self._xpath_ns('./head/paramGroup', namespace)):
            group_id = param_group.get(self._xpath_ns(
                'id', 'http://www.w3.org/XML/1998/namespace'))
            params = {}
            for param in param_group:
                params[param.get('name')] = param.get('value')
            param_groups[group_id] = params

        formats = []
        for video in smil.findall(self._xpath_ns('.//video', namespace)):
            src = video.get('src')
            if not src:
                continue
            bitrate = int_or_none(self._search_regex(r'_(\d+)k', src, 'bitrate', None)) or float_or_none(video.get('system-bitrate') or video.get('systemBitrate'), 1000)
            group_id = video.get('paramGroup')
            param_group = param_groups[group_id]
            for proto in param_group['protocols'].split(','):
                formats.append({
                    'url': '%s://%s' % (proto, param_group['host']),
                    'app': param_group['app'],
                    'play_path': src,
                    'ext': 'flv',
                    'format_id': '%s-%d' % (proto, bitrate),
                    'tbr': bitrate,
                })
        self._sort_formats(formats)
        return formats

    def extract_from_xml_url(self, video_id, xml_url):
        doc = self._download_xml(
            xml_url, video_id,
            note='Downloading video info',
            errnote='Failed to download video info')

        status_code = xpath_text(doc, './status/statuscode')
        if status_code and status_code != 'ok':
            if status_code == 'notVisibleAnymore':
                message = 'Video %s is not available' % video_id
            else:
                message = '%s returned error: %s' % (self.IE_NAME, status_code)
            raise ExtractorError(message, expected=True)

        title = xpath_text(doc, './/information/title', 'title', True)

        urls = []
        formats = []
        for fnode in doc.findall('.//formitaeten/formitaet'):
            video_url = xpath_text(fnode, 'url')
            if not video_url or video_url in urls:
                continue
            urls.append(video_url)

            is_available = 'http://www.metafilegenerator' not in video_url
            geoloced = 'static_geoloced_online' in video_url
            if not is_available or geoloced:
                continue

            format_id = fnode.attrib['basetype']
            format_m = re.match(r'''(?x)
                (?P<vcodec>[^_]+)_(?P<acodec>[^_]+)_(?P<container>[^_]+)_
                (?P<proto>[^_]+)_(?P<index>[^_]+)_(?P<indexproto>[^_]+)
            ''', format_id)

            ext = determine_ext(video_url, None) or format_m.group('container')

            if ext == 'meta':
                continue
            elif ext == 'smil':
                formats.extend(self._extract_smil_formats(
                    video_url, video_id, fatal=False))
            elif ext == 'm3u8':
                # the certificates are misconfigured (see
                # https://github.com/ytdl-org/youtube-dl/issues/8665)
                if video_url.startswith('https://'):
                    continue
                formats.extend(self._extract_m3u8_formats(
                    video_url, video_id, 'mp4', 'm3u8_native',
                    m3u8_id=format_id, fatal=False))
            elif ext == 'f4m':
                formats.extend(self._extract_f4m_formats(
                    video_url, video_id, f4m_id=format_id, fatal=False))
            else:
                quality = xpath_text(fnode, './quality')
                if quality:
                    format_id += '-' + quality

                abr = int_or_none(xpath_text(fnode, './audioBitrate'), 1000)
                vbr = int_or_none(xpath_text(fnode, './videoBitrate'), 1000)

                tbr = int_or_none(self._search_regex(
                    r'_(\d+)k', video_url, 'bitrate', None))
                if tbr and vbr and not abr:
                    abr = tbr - vbr

                formats.append({
                    'format_id': format_id,
                    'url': video_url,
                    'ext': ext,
                    'acodec': format_m.group('acodec'),
                    'vcodec': format_m.group('vcodec'),
                    'abr': abr,
                    'vbr': vbr,
                    'tbr': tbr,
                    'width': int_or_none(xpath_text(fnode, './width')),
                    'height': int_or_none(xpath_text(fnode, './height')),
                    'filesize': int_or_none(xpath_text(fnode, './filesize')),
                    'protocol': format_m.group('proto').lower(),
                })

        geolocation = xpath_text(doc, './/details/geolocation')
        if not formats and geolocation and geolocation != 'none':
            self.raise_geo_restricted(countries=self._GEO_COUNTRIES)

        self._sort_formats(formats)

        thumbnails = []
        for node in doc.findall('.//teaserimages/teaserimage'):
            thumbnail_url = node.text
            if not thumbnail_url:
                continue
            thumbnail = {
                'url': thumbnail_url,
            }
            thumbnail_key = node.get('key')
            if thumbnail_key:
                m = re.match('^([0-9]+)x([0-9]+)$', thumbnail_key)
                if m:
                    thumbnail['width'] = int(m.group(1))
                    thumbnail['height'] = int(m.group(2))
            thumbnails.append(thumbnail)

        upload_date = unified_strdate(xpath_text(doc, './/details/airtime'))

        return {
            'id': video_id,
            'title': title,
            'description': xpath_text(doc, './/information/detail'),
            'duration': int_or_none(xpath_text(doc, './/details/lengthSec')),
            'thumbnails': thumbnails,
            'uploader': xpath_text(doc, './/details/originChannelTitle'),
            'uploader_id': xpath_text(doc, './/details/originChannelId'),
            'upload_date': upload_date,
            'formats': formats,
        }

    def _real_extract(self, url):
        video_id = self._match_id(url)
        details_url = 'http://www.3sat.de/mediathek/xmlservice/web/beitragsDetails?id=%s' % video_id
        return self.extract_from_xml_url(video_id, details_url)


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

import os.path
import re

from .common import InfoExtractor
from ..compat import compat_urllib_parse_unquote
from ..utils import url_basename

class DropboxIE(InfoExtractor):
    _VALID_URL = r'https?://(?:www\.)?dropbox[.]com/sh?/(?P<id>[a-zA-Z0-9]{15})/.*'
    _TESTS = [
        {
            'url': 'https://www.dropbox.com/s/nelirfsxnmcfbfh/youtube-dl%20test%20video%20%27%C3%A4%22BaW_jenozKc.mp4?dl=0',
            'info_dict': {
                'id': 'nelirfsxnmcfbfh',
                'ext': 'mp4',
                'title': 'youtube-dl test video \'ä"BaW_jenozKc'
            }
        }, {
            'url': 'https://www.dropbox.com/sh/662glsejgzoj9sr/AAByil3FGH9KFNZ13e08eSa1a/Pregame%20Ceremony%20Program%20PA%2020140518.m4v',
            'only_matching': True,
        },
    ]

    def _real_extract(self, url):
        mobj = re.match(self._VALID_URL, url)
        video_id = mobj.group('id')
        fn = compat_urllib_parse_unquote(url_basename(url))
        title = os.path.splitext(fn)[0]
        video_url = re.sub(r'[?&]dl=0', '', url)
        video_url += ('?' if '?' not in video_url else '&') + 'dl=1'

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

repo_name: rrosajp/youtube


code:
from __future__ import unicode_literals

import re

from .common import InfoExtractor
from ..utils import (
    int_or_none,
    NO_DEFAULT,
    parse_duration,
    str_to_int,
)

class DrTuberIE(InfoExtractor):
    _VALID_URL = r'https?://(?:(?:www|m)\.)?drtuber\.com/(?:video|embed)/(?P<id>\d+)(?:/(?P<display_id>[\w-]+))?'
    _TESTS = [{
        'url': 'http://www.drtuber.com/video/1740434/hot-perky-blonde-naked-golf',
        'md5': '93e680cf2536ad0dfb7e74d94a89facd',
        'info_dict': {
            'id': '1740434',
            'display_id': 'hot-perky-blonde-naked-golf',
            'ext': 'mp4',
            'title': 'hot perky blonde naked golf',
            'like_count': int,
            'comment_count': int,
            'categories': ['Babe', 'Blonde', 'Erotic', 'Outdoor', 'Softcore', 'Solo'],
            'thumbnail': r're:https?://.*\.jpg$',
            'age_limit': 18,
        }
    }, {
        'url': 'http://www.drtuber.com/embed/489939',
        'only_matching': True,
    }, {
        'url': 'http://m.drtuber.com/video/3893529/lingerie-blowjob-from-beautiful-teen',
        'only_matching': True,
    }]

    @staticmethod
    def _extract_urls(webpage):
        return re.findall(
            r']+?src=["\'](?P<url>(?:https?:)?//(?:www\.)?drtuber\.com/embed/\d+)',
            webpage)

    def _real_extract(self, url):
        mobj = re.match(self._VALID_URL, url)
        video_id = mobj.group('id')
        display_id = mobj.group('display_id') or video_id

        webpage = self._download_webpage(
            'http://www.drtuber.com/video/%s' % video_id, display_id)

        video_data = self._download_json(
            'http://www.drtuber.com/player_config_json/', video_id, query={
                'vid': video_id,
                'embed': 0,
                'aid': 0,
                'domain_id': 0,
            })

        formats = []
        for format_id, video_url in video_data['files'].items():
            if video_url:
                formats.append({
                    'format_id': format_id,
                    'quality': 2 if format_id == 'hq' else 1,
                    'url': video_url
                })
        self._sort_formats(formats)

        duration = int_or_none(video_data.get('duration')) or parse_duration(
            video_data.get('duration_format'))

        title = self._html_search_regex(
            (r'
]+class=["\']title[^>]+>([^<]+)',
             r'<title>([^<]+)\s*@\s+DrTuber',
             r'class="title_watch"[^>]*><(?:p|h\d+)[^>]*>([^<]+)<',
             r'
]+class="title_substrate">([^<]+)
',
             r'<title>([^<]+) - \d+'),
            webpage, 'title')

        thumbnail = self._html_search_regex(
            r'poster="([^"]+)"',
            webpage, 'thumbnail', fatal=False)

        def extract_count(id_, name, default=NO_DEFAULT):
            return str_to_int(self._html_search_regex(
                r']+(?:class|id)="%s"[^>]*>([\d,\.]+)' % id_,
                webpage, '%s count' % name, default=default, fatal=False))

        like_count = extract_count('rate_likes', 'like')
        dislike_count = extract_count('rate_dislikes', 'dislike', default=None)
        comment_count = extract_count('comments_count', 'comment')

        cats_str = self._search_regex(
            r'
]+class="categories_list">(.+?)
',
            webpage, 'categories', fatal=False)
        categories = [] if not cats_str else re.findall(
            r'<a title="([^"]+)"', cats_str)

        return {
            'id': video_id,
            'display_id': display_id,
            'formats': formats,
            'title': title,
            'thumbnail': thumbnail,
            'like_count': like_count,
            'dislike_count': dislike_count,
            'comment_count': comment_count,
            'categories': categories,
            'age_limit': self._rta_search(webpage),
            'duration': duration,
        }


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

import binascii
import hashlib
import re

from .common import InfoExtractor
from ..aes import aes_cbc_decrypt
from ..compat import compat_urllib_parse_unquote
from ..utils import (
    bytes_to_intlist,
    ExtractorError,
    int_or_none,
    intlist_to_bytes,
    float_or_none,
    mimetype2ext,
    str_or_none,
    try_get,
    unified_timestamp,
    update_url_query,
    url_or_none,
)

class DRTVIE(InfoExtractor):
    _VALID_URL = r'''(?x)
                    https?://
                        (?:
                            (?:www\.)?dr\.dk/(?:tv/se|nyheder|radio(?:/ondemand)?)/(?:[^/]+/)*|
                            (?:www\.)?(?:dr\.dk|dr-massive\.com)/drtv/(?:se|episode)/
                        )
                        (?P<id>[\da-z_-]+)
                    '''
    _GEO_BYPASS = False
    _GEO_COUNTRIES = ['DK']
    IE_NAME = 'drtv'
    _TESTS = [{
        'url': 'https://www.dr.dk/tv/se/boern/ultra/klassen-ultra/klassen-darlig-taber-10',
        'md5': '25e659cccc9a2ed956110a299fdf5983',
        'info_dict': {
            'id': 'klassen-darlig-taber-10',
            'ext': 'mp4',
            'title': 'Klassen - Dårlig taber (10)',
            'description': 'md5:815fe1b7fa656ed80580f31e8b3c79aa',
            'timestamp': 1539085800,
            'upload_date': '20181009',
            'duration': 606.84,
            'series': 'Klassen',
            'season': 'Klassen I',
            'season_number': 1,
            'season_id': 'urn:dr:mu:bundle:57d7e8216187a4031cfd6f6b',
            'episode': 'Episode 10',
            'episode_number': 10,
            'release_year': 2016,
        },
        'expected_warnings': ['Unable to download f4m manifest'],
    }, {
        # embed
        'url': 'https://www.dr.dk/nyheder/indland/live-christianias-rydning-af-pusher-street-er-i-gang',
        'info_dict': {
            'id': 'urn:dr:mu:programcard:57c926176187a50a9c6e83c6',
            'ext': 'mp4',
            'title': 'christiania pusher street ryddes drdkrjpo',
            'description': 'md5:2a71898b15057e9b97334f61d04e6eb5',
            'timestamp': 1472800279,
            'upload_date': '20160902',
            'duration': 131.4,
        },
        'params': {
            'skip_download': True,
        },
        'expected_warnings': ['Unable to download f4m manifest'],
    }, {
        # with SignLanguage formats
        'url': 'https://www.dr.dk/tv/se/historien-om-danmark/-/historien-om-danmark-stenalder',
        'info_dict': {
            'id': 'historien-om-danmark-stenalder',
            'ext': 'mp4',
            'title': 'Historien om Danmark: Stenalder',
            'description': 'md5:8c66dcbc1669bbc6f873879880f37f2a',
            'timestamp': 1546628400,
            'upload_date': '20190104',
            'duration': 3502.56,
            'formats': 'mincount:20',
        },
        'params': {
            'skip_download': True,
        },
    }, {
        'url': 'https://www.dr.dk/radio/p4kbh/regionale-nyheder-kh4/p4-nyheder-2019-06-26-17-30-9',
        'only_matching': True,
    }, {
        'url': 'https://www.dr.dk/drtv/se/bonderoeven_71769',
        'info_dict': {
            'id': '00951930010',
            'ext': 'mp4',
            'title': 'Bonderøven (1:8)',
            'description': 'md5:3cf18fc0d3b205745d4505f896af8121',
            'timestamp': 1546542000,
            'upload_date': '20190103',
            'duration': 2576.6,
        },
        'params': {
            'skip_download': True,
        },
    }, {
        'url': 'https://www.dr.dk/drtv/episode/bonderoeven_71769',
        'only_matching': True,
    }, {
        'url': 'https://dr-massive.com/drtv/se/bonderoeven_71769',
        'only_matching': True,
    }]

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

        webpage = self._download_webpage(url, video_id)

        if '>Programmet er ikke længere tilgængeligt' in webpage:
            raise ExtractorError(
                'Video %s is not available' % video_id, expected=True)

        video_id = self._search_regex(
            (r'data-(?:material-identifier|episode-slug)="([^"]+)"',
             r'data-resource="[^>"]+mu/programcard/expanded/([^"]+)"'),
            webpage, 'video id', default=None)

        if not video_id:
            video_id = self._search_regex(
                r'(urn(?:%3A|:)dr(?:%3A|:)mu(?:%3A|:)programcard(?:%3A|:)[\da-f]+)',
                webpage, 'urn', default=None)
            if video_id:
                video_id = compat_urllib_parse_unquote(video_id)

        _PROGRAMCARD_BASE = 'https://www.dr.dk/mu-online/api/1.4/programcard'
        query = {'expanded': 'true'}

        if video_id:
            programcard_url = '%s/%s' % (_PROGRAMCARD_BASE, video_id)
        else:
            programcard_url = _PROGRAMCARD_BASE
            page = self._parse_json(
                self._search_regex(
                    r'data\s*=\s*({.+?})\s*(?:;|</script)', webpage,
                    'data'), '1')['cache']['page']
            page = page[list(page.keys())[0]]
            item = try_get(
                page, (lambda x: x['item'], lambda x: x['entries'][0]['item']),
                dict)
            video_id = item['customId'].split(':')[-1]
            query['productionnumber'] = video_id

        data = self._download_json(
            programcard_url, video_id, 'Downloading video JSON', query=query)

        title = str_or_none(data.get('Title')) or re.sub(
            r'\s*\|\s*(?:TV\s*\|\s*DR|DRTV)$', '',
            self._og_search_title(webpage))
        description = self._og_search_description(
            webpage, default=None) or data.get('Description')

        timestamp = unified_timestamp(
            data.get('PrimaryBroadcastStartTime') or data.get('SortDateTime'))

        thumbnail = None
        duration = None

        restricted_to_denmark = False

        formats = []
        subtitles = {}

        assets = []
        primary_asset = data.get('PrimaryAsset')
        if isinstance(primary_asset, dict):
            assets.append(primary_asset)
        secondary_assets = data.get('SecondaryAssets')
        if isinstance(secondary_assets, list):
            for secondary_asset in secondary_assets:
                if isinstance(secondary_asset, dict):
                    assets.append(secondary_asset)

        def hex_to_bytes(hex):
            return binascii.a2b_hex(hex.encode('ascii'))

        def decrypt_uri(e):
            n = int(e[2:10], 16)
            a = e[10 + n:]
            data = bytes_to_intlist(hex_to_bytes(e[10:10 + n]))
            key = bytes_to_intlist(hashlib.sha256(
                ('%s:sRBzYNXBzkKgnjj8pGtkACch' % a).encode('utf-8')).digest())
            iv = bytes_to_intlist(hex_to_bytes(a))
            decrypted = aes_cbc_decrypt(data, key, iv)
            return intlist_to_bytes(
                decrypted[:-decrypted[-1]]).decode('utf-8').split('?')[0]

        for asset in assets:
            kind = asset.get('Kind')
            if kind == 'Image':
                thumbnail = url_or_none(asset.get('Uri'))
            elif kind in ('VideoResource', 'AudioResource'):
                duration = float_or_none(asset.get('DurationInMilliseconds'), 1000)
                restricted_to_denmark = asset.get('RestrictedToDenmark')
                asset_target = asset.get('Target')
                for link in asset.get('Links', []):
                    uri = link.get('Uri')
                    if not uri:
                        encrypted_uri = link.get('EncryptedUri')
                        if not encrypted_uri:
                            continue
                        try:
                            uri = decrypt_uri(encrypted_uri)
                        except Exception:
                            self.report_warning(
                                'Unable to decrypt EncryptedUri', video_id)
                            continue
                    uri = url_or_none(uri)
                    if not uri:
                        continue
                    target = link.get('Target')
                    format_id = target or ''
                    if asset_target in ('SpokenSubtitles', 'SignLanguage', 'VisuallyInterpreted'):
                        preference = -1
                        format_id += '-%s' % asset_target
                    elif asset_target == 'Default':
                        preference = 1
                    else:
                        preference = None
                    if target == 'HDS':
                        f4m_formats = self._extract_f4m_formats(
                            uri + '?hdcore=3.3.0&plugin=aasp-3.3.0.99.43',
                            video_id, preference, f4m_id=format_id, fatal=False)
                        if kind == 'AudioResource':
                            for f in f4m_formats:
                                f['vcodec'] = 'none'
                        formats.extend(f4m_formats)
                    elif target == 'HLS':
                        formats.extend(self._extract_m3u8_formats(
                            uri, video_id, 'mp4', entry_protocol='m3u8_native',
                            preference=preference, m3u8_id=format_id,
                            fatal=False))
                    else:
                        bitrate = link.get('Bitrate')
                        if bitrate:
                            format_id += '-%s' % bitrate
                        formats.append({
                            'url': uri,
                            'format_id': format_id,
                            'tbr': int_or_none(bitrate),
                            'ext': link.get('FileFormat'),
                            'vcodec': 'none' if kind == 'AudioResource' else None,
                            'preference': preference,
                        })
            subtitles_list = asset.get('SubtitlesList') or asset.get('Subtitleslist')
            if isinstance(subtitles_list, list):
                LANGS = {
                    'Danish': 'da',
                }
                for subs in subtitles_list:
                    if not isinstance(subs, dict):
                        continue
                    sub_uri = url_or_none(subs.get('Uri'))
                    if not sub_uri:
                        continue
                    lang = subs.get('Language') or 'da'
                    subtitles.setdefault(LANGS.get(lang, lang), []).append({
                        'url': sub_uri,
                        'ext': mimetype2ext(subs.get('MimeType')) or 'vtt'
                    })

        if not formats and restricted_to_denmark:
            self.raise_geo_restricted(
                'Unfortunately, DR is not allowed to show this program outside Denmark.',
                countries=self._GEO_COUNTRIES)

        self._sort_formats(formats)

        return {
            'id': video_id,
            'title': title,
            'description': description,
            'thumbnail': thumbnail,
            'timestamp': timestamp,
            'duration': duration,
            'formats': formats,
            'subtitles': subtitles,
            'series': str_or_none(data.get('SeriesTitle')),
            'season': str_or_none(data.get('SeasonTitle')),
            'season_number': int_or_none(data.get('SeasonNumber')),
            'season_id': str_or_none(data.get('SeasonUrn')),
            'episode': str_or_none(data.get('EpisodeTitle')),
            'episode_number': int_or_none(data.get('EpisodeNumber')),
            'release_year': int_or_none(data.get('ProductionYear')),
        }

class DRTVLiveIE(InfoExtractor):
    IE_NAME = 'drtv:live'
    _VALID_URL = r'https?://(?:www\.)?dr\.dk/(?:tv|TV)/live/(?P<id>[\da-z-]+)'
    _GEO_COUNTRIES = ['DK']
    _TEST = {
        'url': 'https://www.dr.dk/tv/live/dr1',
        'info_dict': {
            'id': 'dr1',
            'ext': 'mp4',
            'title': 're:^DR1 [0-9]{4}-[0-9]{2}-[0-9]{2} [0-9]{2}:[0-9]{2}$',
        },
        'params': {
            # m3u8 download
            'skip_download': True,
        },
    }

    def _real_extract(self, url):
        channel_id = self._match_id(url)
        channel_data = self._download_json(
            'https://www.dr.dk/mu-online/api/1.0/channel/' + channel_id,
            channel_id)
        title = self._live_title(channel_data['Title'])

        formats = []
        for streaming_server in channel_data.get('StreamingServers', []):
            server = streaming_server.get('Server')
            if not server:
                continue
            link_type = streaming_server.get('LinkType')
            for quality in streaming_server.get('Qualities', []):
                for stream in quality.get('Streams', []):
                    stream_path = stream.get('Stream')
                    if not stream_path:
                        continue
                    stream_url = update_url_query(
                        '%s/%s' % (server, stream_path), {'b': ''})
                    if link_type == 'HLS':
                        formats.extend(self._extract_m3u8_formats(
                            stream_url, channel_id, 'mp4',
                            m3u8_id=link_type, fatal=False, live=True))
                    elif link_type == 'HDS':
                        formats.extend(self._extract_f4m_formats(update_url_query(
                            '%s/%s' % (server, stream_path), {'hdcore': '3.7.0'}),
                            channel_id, f4m_id=link_type, fatal=False))
        self._sort_formats(formats)

        return {
            'id': channel_id,
            'title': title,
            'thumbnail': channel_data.get('PrimaryImageUri'),
            'formats': formats,
            'is_live': True,
        }


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

import json
import re
from socket import timeout

from .common import InfoExtractor
from ..utils import (
    int_or_none,
    parse_iso8601,
)

class DTubeIE(InfoExtractor):
    _VALID_URL = r'https?://(?:www\.)?d\.tube/(?:#!/)?v/(?P<uploader_id>[0-9a-z.-]+)/(?P<id>[0-9a-z]{8})'
    _TEST = {
        'url': 'https://d.tube/#!/v/broncnutz/x380jtr1',
        'md5': '9f29088fa08d699a7565ee983f56a06e',
        'info_dict': {
            'id': 'x380jtr1',
            'ext': 'mp4',
            'title': 'Lefty 3-Rings is Back Baby!! NCAA Picks',
            'description': 'md5:60be222088183be3a42f196f34235776',
            'uploader_id': 'broncnutz',
            'upload_date': '20190107',
            'timestamp': 1546854054,
        },
        'params': {
            'format': '480p',
        },
    }

    def _real_extract(self, url):
        uploader_id, video_id = re.match(self._VALID_URL, url).groups()
        result = self._download_json('https://api.steemit.com/', video_id, data=json.dumps({
            'jsonrpc': '2.0',
            'method': 'get_content',
            'params': [uploader_id, video_id],
        }).encode())['result']

        metadata = json.loads(result['json_metadata'])
        video = metadata['video']
        content = video['content']
        info = video.get('info', {})
        title = info.get('title') or result['title']

        def canonical_url(h):
            if not h:
                return None
            return 'https://video.dtube.top/ipfs/' + h

        formats = []
        for q in ('240', '480', '720', '1080', ''):
            video_url = canonical_url(content.get('video%shash' % q))
            if not video_url:
                continue
            format_id = (q + 'p') if q else 'Source'
            try:
                self.to_screen('%s: Checking %s video format URL' % (video_id, format_id))
                self._downloader._opener.open(video_url, timeout=5).close()
            except timeout:
                self.to_screen(
                    '%s: %s URL is invalid, skipping' % (video_id, format_id))
                continue
            formats.append({
                'format_id': format_id,
                'url': video_url,
                'height': int_or_none(q),
                'ext': 'mp4',
            })

        return {
            'id': video_id,
            'title': title,
            'description': content.get('description'),
            'thumbnail': canonical_url(info.get('snaphash')),
            'tags': content.get('tags') or metadata.get('tags'),
            'duration': info.get('duration'),
            'formats': formats,
            'timestamp': parse_iso8601(result.get('created')),
            'uploader_id': uploader_id,
        }


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

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

class DumpertIE(InfoExtractor):
    _VALID_URL = r'(?P<protocol>https?)://(?:(?:www|legacy)\.)?dumpert\.nl/(?:mediabase|embed|item)/(?P<id>[0-9]+[/_][0-9a-zA-Z]+)'
    _TESTS = [{
        'url': 'https://www.dumpert.nl/item/6646981_951bc60f',
        'md5': '1b9318d7d5054e7dcb9dc7654f21d643',
        'info_dict': {
            'id': '6646981/951bc60f',
            'ext': 'mp4',
            'title': 'Ik heb nieuws voor je',
            'description': 'Niet schrikken hoor',
            'thumbnail': r're:^https?://.*\.jpg$',
        }
    }, {
        'url': 'https://www.dumpert.nl/embed/6675421_dc440fe7',
        'only_matching': True,
    }, {
        'url': 'http://legacy.dumpert.nl/mediabase/6646981/951bc60f',
        'only_matching': True,
    }, {
        'url': 'http://legacy.dumpert.nl/embed/6675421/dc440fe7',
        'only_matching': True,
    }]

    def _real_extract(self, url):
        video_id = self._match_id(url).replace('_', '/')
        item = self._download_json(
            'http://api-live.dumpert.nl/mobile_api/json/info/' + video_id.replace('/', '_'),
            video_id)['items'][0]
        title = item['title']
        media = next(m for m in item['media'] if m.get('mediatype') == 'VIDEO')

        quality = qualities(['flv', 'mobile', 'tablet', '720p'])
        formats = []
        for variant in media.get('variants', []):
            uri = variant.get('uri')
            if not uri:
                continue
            version = variant.get('version')
            formats.append({
                'url': uri,
                'format_id': version,
                'quality': quality(version),
            })
        self._sort_formats(formats)

        thumbnails = []
        stills = item.get('stills') or {}
        for t in ('thumb', 'still'):
            for s in ('', '-medium', '-large'):
                still_id = t + s
                still_url = stills.get(still_id)
                if not still_url:
                    continue
                thumbnails.append({
                    'id': still_id,
                    'url': still_url,
                })

        stats = item.get('stats') or {}

        return {
            'id': video_id,
            'title': title,
            'description': item.get('description'),
            'thumbnails': thumbnails,
            'formats': formats,
            'duration': int_or_none(media.get('duration')),
            'like_count': int_or_none(stats.get('kudos_total')),
            'view_count': int_or_none(stats.get('views_total')),
        }


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

import re

from .common import InfoExtractor
from ..utils import (
    determine_ext,
    ExtractorError,
    int_or_none,
    js_to_json,
    mimetype2ext,
    try_get,
    unescapeHTML,
    parse_iso8601,
)

class DVTVIE(InfoExtractor):
    IE_NAME = 'dvtv'
    IE_DESC = 'http://video.aktualne.cz/'
    _VALID_URL = r'https?://video\.aktualne\.cz/(?:[^/]+/)+r~(?P<id>[0-9a-f]{32})'
    _TESTS = [{
        'url': 'http://video.aktualne.cz/dvtv/vondra-o-ceskem-stoleti-pri-pohledu-na-havla-mi-bylo-trapne/r~e5efe9ca855511e4833a0025900fea04/',
        'md5': '67cb83e4a955d36e1b5d31993134a0c2',
        'info_dict': {
            'id': 'dc0768de855511e49e4b0025900fea04',
            'ext': 'mp4',
            'title': 'Vondra o Českém století: Při pohledu na Havla mi bylo trapně',
            'duration': 1484,
            'upload_date': '20141217',
            'timestamp': 1418792400,
        }
    }, {
        'url': 'http://video.aktualne.cz/dvtv/dvtv-16-12-2014-utok-talibanu-boj-o-kliniku-uprchlici/r~973eb3bc854e11e498be002590604f2e/',
        'info_dict': {
            'title': r'DVTV 16. 12. 2014: útok Talibanu, boj o kliniku, uprchlíci',
            'id': '973eb3bc854e11e498be002590604f2e',
        },
        'playlist': [{
            'md5': 'da7ca6be4935532241fa9520b3ad91e4',
            'info_dict': {
                'id': 'b0b40906854d11e4bdad0025900fea04',
                'ext': 'mp4',
                'title': 'Drtinová Veselovský TV 16. 12. 2014: Témata dne',
                'description': 'md5:0916925dea8e30fe84222582280b47a0',
                'timestamp': 1418760010,
                'upload_date': '20141216',
            }
        }, {
            'md5': '5f7652a08b05009c1292317b449ffea2',
            'info_dict': {
                'id': '420ad9ec854a11e4bdad0025900fea04',
                'ext': 'mp4',
                'title': 'Školní masakr možná změní boj s Talibanem, říká novinářka',
                'description': 'md5:ff2f9f6de73c73d7cef4f756c1c1af42',
                'timestamp': 1418760010,
                'upload_date': '20141216',
            }
        }, {
            'md5': '498eb9dfa97169f409126c617e2a3d64',
            'info_dict': {
                'id': '95d35580846a11e4b6d20025900fea04',
                'ext': 'mp4',
                'title': 'Boj o kliniku: Veřejný zájem, nebo právo na majetek?',
                'description': 'md5:889fe610a70fee5511dc3326a089188e',
                'timestamp': 1418760010,
                'upload_date': '20141216',
            }
        }, {
            'md5': 'b8dc6b744844032dab6ba3781a7274b9',
            'info_dict': {
                'id': '6fe14d66853511e4833a0025900fea04',
                'ext': 'mp4',
                'title': 'Pánek: Odmítání syrských uprchlíků je ostudou české vlády',
                'description': 'md5:544f86de6d20c4815bea11bf2ac3004f',
                'timestamp': 1418760010,
                'upload_date': '20141216',
            }
        }],
    }, {
        'url': 'https://video.aktualne.cz/dvtv/zeman-si-jen-leci-mindraky-sobotku-nenavidi-a-babis-se-mu-te/r~960cdb3a365a11e7a83b0025900fea04/',
        'md5': 'f8efe9656017da948369aa099788c8ea',
        'info_dict': {
            'id': '3c496fec365911e7a6500025900fea04',
            'ext': 'mp4',
            'title': 'Zeman si jen léčí mindráky, Sobotku nenávidí a Babiš se mu teď hodí, tvrdí Kmenta',
            'duration': 1103,
            'upload_date': '20170511',
            'timestamp': 1494514200,
        },
        'params': {
            'skip_download': True,
        },
    }, {
        'url': 'http://video.aktualne.cz/v-cechach-poprve-zazni-zelenkova-zrestaurovana-mse/r~45b4b00483ec11e4883b002590604f2e/',
        'only_matching': True,
    }, {
        # Test live stream video (liveStarter) parsing
        'url': 'https://video.aktualne.cz/dvtv/zive-mistryne-sveta-eva-samkova-po-navratu-ze-sampionatu/r~182654c2288811e990fd0cc47ab5f122/',
        'md5': '2e552e483f2414851ca50467054f9d5d',
        'info_dict': {
            'id': '8d116360288011e98c840cc47ab5f122',
            'ext': 'mp4',
            'title': 'Živě: Mistryně světa Eva Samková po návratu ze šampionátu',
            'upload_date': '20190204',
            'timestamp': 1549289591,
        },
        'params': {
            # Video content is no longer available
            'skip_download': True,
        },
    }]

    def _parse_video_metadata(self, js, video_id, timestamp):
        data = self._parse_json(js, video_id, transform_source=js_to_json)
        title = unescapeHTML(data['title'])

        live_starter = try_get(data, lambda x: x['plugins']['liveStarter'], dict)
        if live_starter:
            data.update(live_starter)

        formats = []
        for tracks in data.get('tracks', {}).values():
            for video in tracks:
                video_url = video.get('src')
                if not video_url:
                    continue
                video_type = video.get('type')
                ext = determine_ext(video_url, mimetype2ext(video_type))
                if video_type == 'application/vnd.apple.mpegurl' or ext == 'm3u8':
                    formats.extend(self._extract_m3u8_formats(
                        video_url, video_id, 'mp4', entry_protocol='m3u8_native',
                        m3u8_id='hls', fatal=False))
                elif video_type == 'application/dash+xml' or ext == 'mpd':
                    formats.extend(self._extract_mpd_formats(
                        video_url, video_id, mpd_id='dash', fatal=False))
                else:
                    label = video.get('label')
                    height = self._search_regex(
                        r'^(\d+)[pP]', label or '', 'height', default=None)
                    format_id = ['http']
                    for f in (ext, label):
                        if f:
                            format_id.append(f)
                    formats.append({
                        'url': video_url,
                        'format_id': '-'.join(format_id),
                        'height': int_or_none(height),
                    })
        self._sort_formats(formats)

        return {
            'id': data.get('mediaid') or video_id,
            'title': title,
            'description': data.get('description'),
            'thumbnail': data.get('image'),
            'duration': int_or_none(data.get('duration')),
            'timestamp': int_or_none(timestamp),
            'formats': formats
        }

    def _real_extract(self, url):
        video_id = self._match_id(url)
        webpage = self._download_webpage(url, video_id)
        timestamp = parse_iso8601(self._html_search_meta(
            'article:published_time', webpage, 'published time', default=None))

        items = re.findall(r'(?s)playlist\.push\(({.+?})\);', webpage)
        if items:
            return self.playlist_result(
                [self._parse_video_metadata(i, video_id, timestamp) for i in items],
                video_id, self._html_search_meta('twitter:title', webpage))

        item = self._search_regex(
            r'(?s)BBXPlayer\.setup\((.+?)\);',
            webpage, 'video', default=None)
        if item:
            # remove function calls (ex. htmldeentitize)
            # TODO this should be fixed in a general way in the js_to_json
            item = re.sub(r'\w+?\((.+)\)', r'\1', item)
            return self._parse_video_metadata(item, video_id, timestamp)

        raise ExtractorError('Could not find neither video nor playlist')


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

from .common import InfoExtractor
from ..utils import (
    int_or_none,
    unified_strdate,
)
from ..compat import compat_urlparse

class DWIE(InfoExtractor):
    IE_NAME = 'dw'
    _VALID_URL = r'https?://(?:www\.)?dw\.com/(?:[^/]+/)+(?:av|e)-(?P<id>\d+)'
    _TESTS = [{
        # video
        'url': 'http://www.dw.com/en/intelligent-light/av-19112290',
        'md5': '7372046e1815c5a534b43f3c3c36e6e9',
        'info_dict': {
            'id': '19112290',
            'ext': 'mp4',
            'title': 'Intelligent light',
            'description': 'md5:90e00d5881719f2a6a5827cb74985af1',
            'upload_date': '20160311',
        }
    }, {
        # audio
        'url': 'http://www.dw.com/en/worldlink-my-business/av-19111941',
        'md5': '2814c9a1321c3a51f8a7aeb067a360dd',
        'info_dict': {
            'id': '19111941',
            'ext': 'mp3',
            'title': 'WorldLink: My business',
            'description': 'md5:bc9ca6e4e063361e21c920c53af12405',
            'upload_date': '20160311',
        }
    }, {
        # DW documentaries, only last for one or two weeks
        'url': 'http://www.dw.com/en/documentaries-welcome-to-the-90s-2016-05-21/e-19220158-9798',
        'md5': '56b6214ef463bfb9a3b71aeb886f3cf1',
        'info_dict': {
            'id': '19274438',
            'ext': 'mp4',
            'title': 'Welcome to the 90s – Hip Hop',
            'description': 'Welcome to the 90s - The Golden Decade of Hip Hop',
            'upload_date': '20160521',
        },
        'skip': 'Video removed',
    }]

    def _real_extract(self, url):
        media_id = self._match_id(url)
        webpage = self._download_webpage(url, media_id)
        hidden_inputs = self._hidden_inputs(webpage)
        title = hidden_inputs['media_title']
        media_id = hidden_inputs.get('media_id') or media_id

        if hidden_inputs.get('player_type') == 'video' and hidden_inputs.get('stream_file') == '1':
            formats = self._extract_smil_formats(
                'http://www.dw.com/smil/v-%s' % media_id, media_id,
                transform_source=lambda s: s.replace(
                    'rtmp://tv-od.dw.de/flash/',
                    'http://tv-download.dw.de/dwtv_video/flv/'))
            self._sort_formats(formats)
        else:
            formats = [{'url': hidden_inputs['file_name']}]

        upload_date = hidden_inputs.get('display_date')
        if not upload_date:
            upload_date = self._html_search_regex(
                r'<span[^>]+class="date">([0-9.]+)\s*\|', webpage,
                'upload date', default=None)
            upload_date = unified_strdate(upload_date)

        return {
            'id': media_id,
            'title': title,
            'description': self._og_search_description(webpage),
            'thumbnail': hidden_inputs.get('preview_image'),
            'duration': int_or_none(hidden_inputs.get('file_duration')),
            'upload_date': upload_date,
            'formats': formats,
        }

class DWArticleIE(InfoExtractor):
    IE_NAME = 'dw:article'
    _VALID_URL = r'https?://(?:www\.)?dw\.com/(?:[^/]+/)+a-(?P<id>\d+)'
    _TEST = {
        'url': 'http://www.dw.com/en/no-hope-limited-options-for-refugees-in-idomeni/a-19111009',
        'md5': '8ca657f9d068bbef74d6fc38b97fc869',
        'info_dict': {
            'id': '19105868',
            'ext': 'mp4',
            'title': 'The harsh life of refugees in Idomeni',
            'description': 'md5:196015cc7e48ebf474db9399420043c7',
            'upload_date': '20160310',
        }
    }

    def _real_extract(self, url):
        article_id = self._match_id(url)
        webpage = self._download_webpage(url, article_id)
        hidden_inputs = self._hidden_inputs(webpage)
        media_id = hidden_inputs['media_id']
        media_path = self._search_regex(r'href="([^"]+av-%s)"\s+class="overlayLink"' % media_id, webpage, 'media url')
        media_url = compat_urlparse.urljoin(url, media_path)
        return self.url_result(media_url, 'DW', media_id)


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

import re

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

class EaglePlatformIE(InfoExtractor):
    _VALID_URL = r'''(?x)
                    (?:
                        eagleplatform:(?P<custom_host>[^/]+):|
                        https?://(?P<host>.+?\.media\.eagleplatform\.com)/index/player\?.*\brecord_id=
                    )
                    (?P<id>\d+)
                '''
    _TESTS = [{
        # http://lenta.ru/news/2015/03/06/navalny/
        'url': 'http://lentaru.media.eagleplatform.com/index/player?player=new&record_id=227304&player_template_id=5201',
        # Not checking MD5 as sometimes the direct HTTP link results in 404 and HLS is used
        'info_dict': {
            'id': '227304',
            'ext': 'mp4',
            'title': 'Навальный вышел на свободу',
            'description': 'md5:d97861ac9ae77377f3f20eaf9d04b4f5',
            'thumbnail': r're:^https?://.*\.jpg$',
            'duration': 87,
            'view_count': int,
            'age_limit': 0,
        },
    }, {
        # http://muz-tv.ru/play/7129/
        # http://media.clipyou.ru/index/player?record_id=12820&width=730&height=415&autoplay=true
        'url': 'eagleplatform:media.clipyou.ru:12820',
        'md5': '358597369cf8ba56675c1df15e7af624',
        'info_dict': {
            'id': '12820',
            'ext': 'mp4',
            'title': "'O Sole Mio",
            'thumbnail': r're:^https?://.*\.jpg$',
            'duration': 216,
            'view_count': int,
        },
        'skip': 'Georestricted',
    }, {
        # referrer protected video (https://tvrain.ru/lite/teleshow/kak_vse_nachinalos/namin-418921/)
        'url': 'eagleplatform:tvrainru.media.eagleplatform.com:582306',
        'only_matching': True,
    }]

    @staticmethod
    def _extract_url(webpage):
        # Regular iframe embedding
        mobj = re.search(
            r'<iframe[^>]+src=(["\'])(?P<url>(?:https?:)?//.+?\.media\.eagleplatform\.com/index/player\?.+?)\1',
            webpage)
        if mobj is not None:
            return mobj.group('url')
        PLAYER_JS_RE = r'''
                        <script[^>]+
                            src=(?P<qjs>["\'])(?:https?:)?//(?P<host>(?:(?!(?P=qjs)).)+\.media\.eagleplatform\.com)/player/player\.js(?P=qjs)
                        .+?
                    '''
        # "Basic usage" embedding (see http://dultonmedia.github.io/eplayer/)
        mobj = re.search(
            r'''(?xs)
                    %s
                    <div[^>]+
                        class=(?P<qclass>["\'])eagleplayer(?P=qclass)[^>]+
                        data-id=["\'](?P<id>\d+)
            ''' % PLAYER_JS_RE, webpage)
        if mobj is not None:
            return 'eagleplatform:%(host)s:%(id)s' % mobj.groupdict()
        # Generalization of "Javascript code usage", "Combined usage" and
        # "Usage without attaching to DOM" embeddings (see
        # http://dultonmedia.github.io/eplayer/)
        mobj = re.search(
            r'''(?xs)
                    %s
                    <script>
                    .+?
                    new\s+EaglePlayer\(
                        (?:[^,]+\s*,\s*)?
                        {
                            .+?
                            \bid\s*:\s*["\']?(?P<id>\d+)
                            .+?
                        }
                    \s*\)
                    .+?
                    </script>
            ''' % PLAYER_JS_RE, webpage)
        if mobj is not None:
            return 'eagleplatform:%(host)s:%(id)s' % mobj.groupdict()

    @staticmethod
    def _handle_error(response):
        status = int_or_none(response.get('status', 200))
        if status != 200:
            raise ExtractorError(' '.join(response['errors']), expected=True)

    def _download_json(self, url_or_request, video_id, *args, **kwargs):
        try:
            response = super(EaglePlatformIE, self)._download_json(
                url_or_request, video_id, *args, **kwargs)
        except ExtractorError as ee:
            if isinstance(ee.cause, compat_HTTPError):
                response = self._parse_json(ee.cause.read().decode('utf-8'), video_id)
                self._handle_error(response)
            raise
        return response

    def _get_video_url(self, url_or_request, video_id, note='Downloading JSON metadata'):
        return self._download_json(url_or_request, video_id, note)['data'][0]

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

        mobj = re.match(self._VALID_URL, url)
        host, video_id = mobj.group('custom_host') or mobj.group('host'), mobj.group('id')

        headers = {}
        query = {
            'id': video_id,
        }

        referrer = smuggled_data.get('referrer')
        if referrer:
            headers['Referer'] = referrer
            query['referrer'] = referrer

        player_data = self._download_json(
            'http://%s/api/player_data' % host, video_id,
            headers=headers, query=query)

        media = player_data['data']['playlist']['viewports'][0]['medialist'][0]

        title = media['title']
        description = media.get('description')
        thumbnail = self._proto_relative_url(media.get('snapshot'), 'http:')
        duration = int_or_none(media.get('duration'))
        view_count = int_or_none(media.get('views'))

        age_restriction = media.get('age_restriction')
        age_limit = None
        if age_restriction:
            age_limit = 0 if age_restriction == 'allow_all' else 18

        secure_m3u8 = self._proto_relative_url(media['sources']['secure_m3u8']['auto'], 'http:')

        formats = []

        m3u8_url = self._get_video_url(secure_m3u8, video_id, 'Downloading m3u8 JSON')
        m3u8_formats = self._extract_m3u8_formats(
            m3u8_url, video_id, 'mp4', entry_protocol='m3u8_native',
            m3u8_id='hls', fatal=False)
        formats.extend(m3u8_formats)

        m3u8_formats_dict = {}
        for f in m3u8_formats:
            if f.get('height') is not None:
                m3u8_formats_dict[f['height']] = f

        mp4_data = self._download_json(
            # Secure mp4 URL is constructed according to Player.prototype.mp4 from
            # http://lentaru.media.eagleplatform.com/player/player.js
            re.sub(r'm3u8|hlsvod|hls|f4m', 'mp4s', secure_m3u8),
            video_id, 'Downloading mp4 JSON', fatal=False)
        if mp4_data:
            for format_id, format_url in mp4_data.get('data', {}).items():
                if not url_or_none(format_url):
                    continue
                height = int_or_none(format_id)
                if height is not None and m3u8_formats_dict.get(height):
                    f = m3u8_formats_dict[height].copy()
                    f.update({
                        'format_id': f['format_id'].replace('hls', 'http'),
                        'protocol': 'http',
                    })
                else:
                    f = {
                        'format_id': 'http-%s' % format_id,
                        'height': int_or_none(format_id),
                    }
                f['url'] = format_url
                formats.append(f)

        self._sort_formats(formats)

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


code:
from __future__ import unicode_literals

from .common import InfoExtractor

class EbaumsWorldIE(InfoExtractor):
    _VALID_URL = r'https?://(?:www\.)?ebaumsworld\.com/videos/[^/]+/(?P<id>\d+)'

    _TEST = {
        'url': 'http://www.ebaumsworld.com/videos/a-giant-python-opens-the-door/83367677/',
        'info_dict': {
            'id': '83367677',
            'ext': 'mp4',
            'title': 'A Giant Python Opens The Door',
            'description': 'This is how nightmares start...',
            'uploader': 'jihadpizza',
        },
    }

    def _real_extract(self, url):
        video_id = self._match_id(url)
        config = self._download_xml(
            'http://www.ebaumsworld.com/video/player/%s' % video_id, video_id)
        video_url = config.find('file').text

        return {
            'id': video_id,
            'title': config.find('title').text,
            'url': video_url,
            'description': config.find('description').text,
            'thumbnail': config.find('image').text,
            'uploader': config.find('username').text,
        }


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

import re

from .common import InfoExtractor

class EchoMskIE(InfoExtractor):
    _VALID_URL = r'https?://(?:www\.)?echo\.msk\.ru/sounds/(?P<id>\d+)'
    _TEST = {
        'url': 'http://www.echo.msk.ru/sounds/1464134.html',
        'md5': '2e44b3b78daff5b458e4dbc37f191f7c',
        'info_dict': {
            'id': '1464134',
            'ext': 'mp3',
            'title': 'Особое мнение - 29 декабря 2014, 19:08',
        },
    }

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

        webpage = self._download_webpage(url, video_id)

        audio_url = self._search_regex(
            r'', webpage, 'audio URL')

        title = self._html_search_regex(
            r'([^<]+)',
            webpage, 'title')

        air_date = self._html_search_regex(
            r'(?s)
(.+?)
',
            webpage, 'date', fatal=False, default=None)

        if air_date:
            air_date = re.sub(r'(\s)\1+', r'\1', air_date)
            if air_date:
                title = '%s - %s' % (title, air_date)

        return {
            'id': video_id,
            'url': audio_url,
            'title': title,
        }


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

from .common import InfoExtractor
from ..compat import compat_str
from ..utils import (
    determine_ext,
    int_or_none,
    try_get,
    unified_timestamp,
    url_or_none,
)

class EggheadCourseIE(InfoExtractor):
    IE_DESC = 'egghead.io course'
    IE_NAME = 'egghead:course'
    _VALID_URL = r'https://egghead\.io/courses/(?P<id>[^/?#&]+)'
    _TEST = {
        'url': 'https://egghead.io/courses/professor-frisby-introduces-composable-functional-javascript',
        'playlist_count': 29,
        'info_dict': {
            'id': '72',
            'title': 'Professor Frisby Introduces Composable Functional JavaScript',
            'description': 're:(?s)^This course teaches the ubiquitous.*You\'ll start composing functionality before you know it.$',
        },
    }

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

        lessons = self._download_json(
            'https://egghead.io/api/v1/series/%s/lessons' % playlist_id,
            playlist_id, 'Downloading course lessons JSON')

        entries = []
        for lesson in lessons:
            lesson_url = url_or_none(lesson.get('http_url'))
            if not lesson_url:
                continue
            lesson_id = lesson.get('id')
            if lesson_id:
                lesson_id = compat_str(lesson_id)
            entries.append(self.url_result(
                lesson_url, ie=EggheadLessonIE.ie_key(), video_id=lesson_id))

        course = self._download_json(
            'https://egghead.io/api/v1/series/%s' % playlist_id,
            playlist_id, 'Downloading course JSON', fatal=False) or {}

        playlist_id = course.get('id')
        if playlist_id:
            playlist_id = compat_str(playlist_id)

        return self.playlist_result(
            entries, playlist_id, course.get('title'),
            course.get('description'))

class EggheadLessonIE(InfoExtractor):
    IE_DESC = 'egghead.io lesson'
    IE_NAME = 'egghead:lesson'
    _VALID_URL = r'https://egghead\.io/(?:api/v1/)?lessons/(?P<id>[^/?#&]+)'
    _TESTS = [{
        'url': 'https://egghead.io/lessons/javascript-linear-data-flow-with-container-style-types-box',
        'info_dict': {
            'id': '1196',
            'display_id': 'javascript-linear-data-flow-with-container-style-types-box',
            'ext': 'mp4',
            'title': 'Create linear data flow with container style types (Box)',
            'description': 'md5:9aa2cdb6f9878ed4c39ec09e85a8150e',
            'thumbnail': r're:^https?:.*\.jpg$',
            'timestamp': 1481296768,
            'upload_date': '20161209',
            'duration': 304,
            'view_count': 0,
            'tags': ['javascript', 'free'],
        },
        'params': {
            'skip_download': True,
            'format': 'bestvideo',
        },
    }, {
        'url': 'https://egghead.io/api/v1/lessons/react-add-redux-to-a-react-application',
        'only_matching': True,
    }]

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

        lesson = self._download_json(
            'https://egghead.io/api/v1/lessons/%s' % display_id, display_id)

        lesson_id = compat_str(lesson['id'])
        title = lesson['title']

        formats = []
        for _, format_url in lesson['media_urls'].items():
            format_url = url_or_none(format_url)
            if not format_url:
                continue
            ext = determine_ext(format_url)
            if ext == 'm3u8':
                formats.extend(self._extract_m3u8_formats(
                    format_url, lesson_id, 'mp4', entry_protocol='m3u8',
                    m3u8_id='hls', fatal=False))
            elif ext == 'mpd':
                formats.extend(self._extract_mpd_formats(
                    format_url, lesson_id, mpd_id='dash', fatal=False))
            else:
                formats.append({
                    'url': format_url,
                })
        self._sort_formats(formats)

        return {
            'id': lesson_id,
            'display_id': display_id,
            'title': title,
            'description': lesson.get('summary'),
            'thumbnail': lesson.get('thumb_nail'),
            'timestamp': unified_timestamp(lesson.get('published_at')),
            'duration': int_or_none(lesson.get('duration')),
            'view_count': int_or_none(lesson.get('plays_count')),
            'tags': try_get(lesson, lambda x: x['tag_list'], list),
            'series': try_get(
                lesson, lambda x: x['series']['title'], compat_str),
            'formats': formats,
        }


code:
from __future__ import unicode_literals

from .common import InfoExtractor
from ..compat import compat_urllib_parse_unquote

class EHowIE(InfoExtractor):
    IE_NAME = 'eHow'
    _VALID_URL = r'https?://(?:www\.)?ehow\.com/[^/_?]*_(?P<id>[0-9]+)'
    _TEST = {
        'url': 'http://www.ehow.com/video_12245069_hardwood-flooring-basics.html',
        'md5': '9809b4e3f115ae2088440bcb4efbf371',
        'info_dict': {
            'id': '12245069',
            'ext': 'flv',
            'title': 'Hardwood Flooring Basics',
            'description': 'Hardwood flooring may be time consuming, but its ultimately a pretty straightforward concept. Learn about hardwood flooring basics with help from a hardware flooring business owner in this free video...',
            'uploader': 'Erick Nathan',
        }
    }

    def _real_extract(self, url):
        video_id = self._match_id(url)
        webpage = self._download_webpage(url, video_id)
        video_url = self._search_regex(
            r'(?:file|source)=(http[^\'"&]*)', webpage, 'video URL')
        final_url = compat_urllib_parse_unquote(video_url)
        uploader = self._html_search_meta('uploader', webpage)
        title = self._og_search_title(webpage).replace(' | eHow', '')

        return {
            'id': video_id,
            'url': final_url,
            'title': title,
            'thumbnail': self._og_search_thumbnail(webpage),
            'description': self._og_search_description(webpage),
            'uploader': uploader,
        }


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

import json
import random

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

class EightTracksIE(InfoExtractor):
    IE_NAME = '8tracks'
    _VALID_URL = r'https?://8tracks\.com/(?P<user>[^/]+)/(?P<id>[^/#]+)(?:#.*)?$'
    _TEST = {
        'name': 'EightTracks',
        'url': 'http://8tracks.com/ytdl/youtube-dl-test-tracks-a',
        'info_dict': {
            'id': '1336550',
            'display_id': 'youtube-dl-test-tracks-a',
            'description': "test chars:  \"'/\\ä↭",
            'title': "youtube-dl test tracks \"'/\\ä↭<>",
        },
        'playlist': [
            {
                'md5': '96ce57f24389fc8734ce47f4c1abcc55',
                'info_dict': {
                    'id': '11885610',
                    'ext': 'm4a',
                    'title': "youtue-dl project<>\"' - youtube-dl test track 1 \"'/\\\u00e4\u21ad",
                    'uploader_id': 'ytdl'
                }
            },
            {
                'md5': '4ab26f05c1f7291ea460a3920be8021f',
                'info_dict': {
                    'id': '11885608',
                    'ext': 'm4a',
                    'title': "youtube-dl project - youtube-dl test track 2 \"'/\\\u00e4\u21ad",
                    'uploader_id': 'ytdl'
                }
            },
            {
                'md5': 'd30b5b5f74217410f4689605c35d1fd7',
                'info_dict': {
                    'id': '11885679',
                    'ext': 'm4a',
                    'title': "youtube-dl project as well - youtube-dl test track 3 \"'/\\\u00e4\u21ad",
                    'uploader_id': 'ytdl'
                }
            },
            {
                'md5': '4eb0a669317cd725f6bbd336a29f923a',
                'info_dict': {
                    'id': '11885680',
                    'ext': 'm4a',
                    'title': "youtube-dl project as well - youtube-dl test track 4 \"'/\\\u00e4\u21ad",
                    'uploader_id': 'ytdl'
                }
            },
            {
                'md5': '1893e872e263a2705558d1d319ad19e8',
                'info_dict': {
                    'id': '11885682',
                    'ext': 'm4a',
                    'title': "PH - youtube-dl test track 5 \"'/\\\u00e4\u21ad",
                    'uploader_id': 'ytdl'
                }
            },
            {
                'md5': 'b673c46f47a216ab1741ae8836af5899',
                'info_dict': {
                    'id': '11885683',
                    'ext': 'm4a',
                    'title': "PH - youtube-dl test track 6 \"'/\\\u00e4\u21ad",
                    'uploader_id': 'ytdl'
                }
            },
            {
                'md5': '1d74534e95df54986da7f5abf7d842b7',
                'info_dict': {
                    'id': '11885684',
                    'ext': 'm4a',
                    'title': "phihag - youtube-dl test track 7 \"'/\\\u00e4\u21ad",
                    'uploader_id': 'ytdl'
                }
            },
            {
                'md5': 'f081f47af8f6ae782ed131d38b9cd1c0',
                'info_dict': {
                    'id': '11885685',
                    'ext': 'm4a',
                    'title': "phihag - youtube-dl test track 8 \"'/\\\u00e4\u21ad",
                    'uploader_id': 'ytdl'
                }
            }
        ]
    }

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

        webpage = self._download_webpage(url, playlist_id)

        data = self._parse_json(
            self._search_regex(
                r"(?s)PAGE\.mix\s*=\s*({.+?});\n", webpage, 'trax information'),
            playlist_id)

        session = str(random.randint(0, 1000000000))
        mix_id = data['id']
        track_count = data['tracks_count']
        duration = data['duration']
        avg_song_duration = float(duration) / track_count
        # duration is sometimes negative, use predefined avg duration
        if avg_song_duration <= 0:
            avg_song_duration = 300
        first_url = 'http://8tracks.com/sets/%s/play?player=sm&mix_id=%s&format=jsonh' % (session, mix_id)
        next_url = first_url
        entries = []

        for i in range(track_count):
            api_json = None
            download_tries = 0

            while api_json is None:
                try:
                    api_json = self._download_webpage(
                        next_url, playlist_id,
                        note='Downloading song information %d/%d' % (i + 1, track_count),
                        errnote='Failed to download song information')
                except ExtractorError:
                    if download_tries > 3:
                        raise
                    else:
                        download_tries += 1
                        self._sleep(avg_song_duration, playlist_id)

            api_data = json.loads(api_json)
            track_data = api_data['set']['track']
            info = {
                'id': compat_str(track_data['id']),
                'url': track_data['track_file_stream_url'],
                'title': track_data['performer'] + ' - ' + track_data['name'],
                'raw_title': track_data['name'],
                'uploader_id': data['user']['login'],
                'ext': 'm4a',
            }
            entries.append(info)

            next_url = 'http://8tracks.com/sets/%s/next?player=sm&mix_id=%s&format=jsonh&track_id=%s' % (
                session, mix_id, track_data['id'])
        return {
            '_type': 'playlist',
            'entries': entries,
            'id': compat_str(mix_id),
            'display_id': playlist_id,
            'title': data.get('name'),
            'description': data.get('description'),
        }


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

import json
import re

from .common import InfoExtractor
from ..compat import (
    compat_b64decode,
    compat_str,
    compat_urlparse,
)
from ..utils import (
    extract_attributes,
    ExtractorError,
    get_elements_by_class,
    urlencode_postdata,
)

class EinthusanIE(InfoExtractor):
    _VALID_URL = r'https?://(?P<host>einthusan\.(?:tv|com|ca))/movie/watch/(?P<id>[^/?#&]+)'
    _TESTS = [{
        'url': 'https://einthusan.tv/movie/watch/9097/',
        'md5': 'ff0f7f2065031b8a2cf13a933731c035',
        'info_dict': {
            'id': '9097',
            'ext': 'mp4',
            'title': 'Ae Dil Hai Mushkil',
            'description': 'md5:33ef934c82a671a94652a9b4e54d931b',
            'thumbnail': r're:^https?://.*\.jpg$',
        }
    }, {
        'url': 'https://einthusan.tv/movie/watch/51MZ/?lang=hindi',
        'only_matching': True,
    }, {
        'url': 'https://einthusan.com/movie/watch/9097/',
        'only_matching': True,
    }, {
        'url': 'https://einthusan.ca/movie/watch/4E9n/?lang=hindi',
        'only_matching': True,
    }]

    # reversed from jsoncrypto.prototype.decrypt() in einthusan-PGMovieWatcher.js
    def _decrypt(self, encrypted_data, video_id):
        return self._parse_json(compat_b64decode((
            encrypted_data[:10] + encrypted_data[-1] + encrypted_data[12:-1]
        )).decode('utf-8'), video_id)

    def _real_extract(self, url):
        mobj = re.match(self._VALID_URL, url)
        host = mobj.group('host')
        video_id = mobj.group('id')

        webpage = self._download_webpage(url, video_id)

        title = self._html_search_regex(r'<h3>([^<]+)</h3>', webpage, 'title')

        player_params = extract_attributes(self._search_regex(
            r'(<section[^>]+id="UIVideoPlayer"[^>]+>)', webpage, 'player parameters'))

        page_id = self._html_search_regex(
            '<html[^>]+data-pageid="([^"]+)"', webpage, 'page ID')
        video_data = self._download_json(
            'https://%s/ajax/movie/watch/%s/' % (host, video_id), video_id,
            data=urlencode_postdata({
                'xEvent': 'UIVideoPlayer.PingOutcome',
                'xJson': json.dumps({
                    'EJOutcomes': player_params['data-ejpingables'],
                    'NativeHLS': False
                }),
                'arcVersion': 3,
                'appVersion': 59,
                'gorilla.csrf.Token': page_id,
            }))['Data']

        if isinstance(video_data, compat_str) and video_data.startswith('/ratelimited/'):
            raise ExtractorError(
                'Download rate reached. Please try again later.', expected=True)

        ej_links = self._decrypt(video_data['EJLinks'], video_id)

        formats = []

        m3u8_url = ej_links.get('HLSLink')
        if m3u8_url:
            formats.extend(self._extract_m3u8_formats(
                m3u8_url, video_id, ext='mp4', entry_protocol='m3u8_native'))

        mp4_url = ej_links.get('MP4Link')
        if mp4_url:
            formats.append({
                'url': mp4_url,
            })

        self._sort_formats(formats)

        description = get_elements_by_class('synopsis', webpage)[0]
        thumbnail = self._html_search_regex(
            r'''<img[^>]+src=(["'])(?P<url>(?!\1).+?/moviecovers/(?!\1).+?)\1''',
            webpage, 'thumbnail url', fatal=False, group='url')
        if thumbnail is not None:
            thumbnail = compat_urlparse.urljoin(url, thumbnail)

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


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

from .common import InfoExtractor
from ..utils import (
    float_or_none,
    int_or_none,
    parse_iso8601,
    sanitized_Request,
)

class EitbIE(InfoExtractor):
    IE_NAME = 'eitb.tv'
    _VALID_URL = r'https?://(?:www\.)?eitb\.tv/(?:eu/bideoa|es/video)/[^/]+/\d+/(?P<id>\d+)'

    _TEST = {
        'url': 'http://www.eitb.tv/es/video/60-minutos-60-minutos-2013-2014/4104995148001/4090227752001/lasa-y-zabala-30-anos/',
        'md5': 'edf4436247185adee3ea18ce64c47998',
        'info_dict': {
            'id': '4090227752001',
            'ext': 'mp4',
            'title': '60 minutos (Lasa y Zabala, 30 años)',
            'description': 'Programa de reportajes de actualidad.',
            'duration': 3996.76,
            'timestamp': 1381789200,
            'upload_date': '20131014',
            'tags': list,
        },
    }

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

        video = self._download_json(
            'http://mam.eitb.eus/mam/REST/ServiceMultiweb/Video/MULTIWEBTV/%s/' % video_id,
            video_id, 'Downloading video JSON')

        media = video['web_media'][0]

        formats = []
        for rendition in media['RENDITIONS']:
            video_url = rendition.get('PMD_URL')
            if not video_url:
                continue
            tbr = float_or_none(rendition.get('ENCODING_RATE'), 1000)
            format_id = 'http'
            if tbr:
                format_id += '-%d' % int(tbr)
            formats.append({
                'url': rendition['PMD_URL'],
                'format_id': format_id,
                'width': int_or_none(rendition.get('FRAME_WIDTH')),
                'height': int_or_none(rendition.get('FRAME_HEIGHT')),
                'tbr': tbr,
            })

        hls_url = media.get('HLS_SURL')
        if hls_url:
            request = sanitized_Request(
                'http://mam.eitb.eus/mam/REST/ServiceMultiweb/DomainRestrictedSecurity/TokenAuth/',
                headers={'Referer': url})
            token_data = self._download_json(
                request, video_id, 'Downloading auth token', fatal=False)
            if token_data:
                token = token_data.get('token')
                if token:
                    formats.extend(self._extract_m3u8_formats(
                        '%s?hdnts=%s' % (hls_url, token), video_id, m3u8_id='hls', fatal=False))

        hds_url = media.get('HDS_SURL')
        if hds_url:
            formats.extend(self._extract_f4m_formats(
                '%s?hdcore=3.7.0' % hds_url.replace('euskalsvod', 'euskalvod'),
                video_id, f4m_id='hds', fatal=False))

        self._sort_formats(formats)

        return {
            'id': video_id,
            'title': media.get('NAME_ES') or media.get('name') or media['NAME_EU'],
            'description': media.get('SHORT_DESC_ES') or video.get('desc_group') or media.get('SHORT_DESC_EU'),
            'thumbnail': media.get('STILL_URL') or media.get('THUMBNAIL_URL'),
            'duration': float_or_none(media.get('LENGTH'), 1000),
            'timestamp': parse_iso8601(media.get('BROADCST_DATE'), ' '),
            'tags': media.get('TAGS'),
            'formats': formats,
        }


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

from .common import InfoExtractor
from ..utils import (
    clean_html,
    extract_attributes,
    float_or_none,
    int_or_none,
    try_get,
)

class EllenTubeBaseIE(InfoExtractor):
    def _extract_data_config(self, webpage, video_id):
        details = self._search_regex(
            r'(<[^>]+\bdata-component=(["\'])[Dd]etails.+?></div>)', webpage,
            'details')
        return self._parse_json(
            extract_attributes(details)['data-config'], video_id)

    def _extract_video(self, data, video_id):
        title = data['title']

        formats = []
        duration = None
        for entry in data.get('media'):
            if entry.get('id') == 'm3u8':
                formats = self._extract_m3u8_formats(
                    entry['url'], video_id, 'mp4',
                    entry_protocol='m3u8_native', m3u8_id='hls')
                duration = int_or_none(entry.get('duration'))
                break
        self._sort_formats(formats)

        def get_insight(kind):
            return int_or_none(try_get(
                data, lambda x: x['insight']['%ss' % kind]))

        return {
            'extractor_key': EllenTubeIE.ie_key(),
            'id': video_id,
            'title': title,
            'description': data.get('description'),
            'duration': duration,
            'thumbnail': data.get('thumbnail'),
            'timestamp': float_or_none(data.get('publishTime'), scale=1000),
            'view_count': get_insight('view'),
            'like_count': get_insight('like'),
            'formats': formats,
        }

class EllenTubeIE(EllenTubeBaseIE):
    _VALID_URL = r'''(?x)
                        (?:
                            ellentube:|
                            https://api-prod\.ellentube\.com/ellenapi/api/item/
                        )
                        (?P<id>[\da-f]{8}-[\da-f]{4}-[\da-f]{4}-[\da-f]{4}-[\da-f]{12})
                    '''
    _TESTS = [{
        'url': 'https://api-prod.ellentube.com/ellenapi/api/item/0822171c-3829-43bf-b99f-d77358ae75e3',
        'md5': '2fabc277131bddafdd120e0fc0f974c9',
        'info_dict': {
            'id': '0822171c-3829-43bf-b99f-d77358ae75e3',
            'ext': 'mp4',
            'title': 'Ellen Meets Las Vegas Survivors Jesus Campos and Stephen Schuck',
            'description': 'md5:76e3355e2242a78ad9e3858e5616923f',
            'thumbnail': r're:^https?://.+?',
            'duration': 514,
            'timestamp': 1508505120,
            'upload_date': '20171020',
            'view_count': int,
            'like_count': int,
        }
    }, {
        'url': 'ellentube:734a3353-f697-4e79-9ca9-bfc3002dc1e0',
        'only_matching': True,
    }]

    def _real_extract(self, url):
        video_id = self._match_id(url)
        data = self._download_json(
            'https://api-prod.ellentube.com/ellenapi/api/item/%s' % video_id,
            video_id)
        return self._extract_video(data, video_id)

class EllenTubeVideoIE(EllenTubeBaseIE):
    _VALID_URL = r'https?://(?:www\.)?ellentube\.com/video/(?P<id>.+?)\.html'
    _TEST = {
        'url': 'https://www.ellentube.com/video/ellen-meets-las-vegas-survivors-jesus-campos-and-stephen-schuck.html',
        'only_matching': True,
    }

    def _real_extract(self, url):
        display_id = self._match_id(url)
        webpage = self._download_webpage(url, display_id)
        video_id = self._extract_data_config(webpage, display_id)['id']
        return self.url_result(
            'ellentube:%s' % video_id, ie=EllenTubeIE.ie_key(),
            video_id=video_id)

class EllenTubePlaylistIE(EllenTubeBaseIE):
    _VALID_URL = r'https?://(?:www\.)?ellentube\.com/(?:episode|studios)/(?P<id>.+?)\.html'
    _TESTS = [{
        'url': 'https://www.ellentube.com/episode/dax-shepard-jordan-fisher-haim.html',
        'info_dict': {
            'id': 'dax-shepard-jordan-fisher-haim',
            'title': "Dax Shepard, 'DWTS' Team Jordan Fisher & Lindsay Arnold, HAIM",
            'description': 'md5:bfc982194dabb3f4e325e43aa6b2e21c',
        },
        'playlist_count': 6,
    }, {
        'url': 'https://www.ellentube.com/studios/macey-goes-rving0.html',
        'only_matching': True,
    }]

    def _real_extract(self, url):
        display_id = self._match_id(url)
        webpage = self._download_webpage(url, display_id)
        data = self._extract_data_config(webpage, display_id)['data']
        feed = self._download_json(
            'https://api-prod.ellentube.com/ellenapi/api/feed/?%s'
            % data['filter'], display_id)
        entries = [
            self._extract_video(elem, elem['id'])
            for elem in feed if elem.get('type') == 'VIDEO' and elem.get('id')]
        return self.playlist_result(
            entries, display_id, data.get('title'),
            clean_html(data.get('description')))


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

from .common import InfoExtractor
from ..utils import strip_jsonp, unified_strdate

class ElPaisIE(InfoExtractor):
    _VALID_URL = r'https?://(?:[^.]+\.)?elpais\.com/.*/(?P<id>[^/#?]+)\.html(?:$|[?#])'
    IE_DESC = 'El País'

    _TESTS = [{
        'url': 'http://blogs.elpais.com/la-voz-de-inaki/2014/02/tiempo-nuevo-recetas-viejas.html',
        'md5': '98406f301f19562170ec071b83433d55',
        'info_dict': {
            'id': 'tiempo-nuevo-recetas-viejas',
            'ext': 'mp4',
            'title': 'Tiempo nuevo, recetas viejas',
            'description': 'De lunes a viernes, a partir de las ocho de la mañana, Iñaki Gabilondo nos cuenta su visión de la actualidad nacional e internacional.',
            'upload_date': '20140206',
        }
    }, {
        'url': 'http://elcomidista.elpais.com/elcomidista/2016/02/24/articulo/1456340311_668921.html#?id_externo_nwl=newsletter_diaria20160303t',
        'md5': '3bd5b09509f3519d7d9e763179b013de',
        'info_dict': {
            'id': '1456340311_668921',
            'ext': 'mp4',
            'title': 'Cómo hacer el mejor café con cafetera italiana',
            'description': 'Que sí, que las cápsulas son cómodas. Pero si le pides algo más a la vida, quizá deberías aprender a usar bien la cafetera italiana. No tienes más que ver este vídeo y seguir sus siete normas básicas.',
            'upload_date': '20160303',
        }
    }, {
        'url': 'http://elpais.com/elpais/2017/01/26/ciencia/1485456786_417876.html',
        'md5': '9c79923a118a067e1a45789e1e0b0f9c',
        'info_dict': {
            'id': '1485456786_417876',
            'ext': 'mp4',
            'title': 'Hallado un barco de la antigua Roma que naufragó en Baleares hace 1.800 años',
            'description': 'La nave portaba cientos de ánforas y se hundió cerca de la isla de Cabrera por razones desconocidas',
            'upload_date': '20170127',
        },
    }, {
        'url': 'http://epv.elpais.com/epv/2017/02/14/programa_la_voz_de_inaki/1487062137_075943.html',
        'info_dict': {
            'id': '1487062137_075943',
            'ext': 'mp4',
            'title': 'Disyuntivas',
            'description': 'md5:a0fb1485c4a6a8a917e6f93878e66218',
            'upload_date': '20170214',
        },
        'params': {
            'skip_download': True,
        },
    }]

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

        prefix = self._html_search_regex(
            r'var\s+url_cache\s*=\s*"([^"]+)";', webpage, 'URL prefix')
        id_multimedia = self._search_regex(
            r"id_multimedia\s*=\s*'([^']+)'", webpage, 'ID multimedia', default=None)
        if id_multimedia:
            url_info = self._download_json(
                'http://elpais.com/vdpep/1/?pepid=' + id_multimedia, video_id, transform_source=strip_jsonp)
            video_suffix = url_info['mp4']
        else:
            video_suffix = self._search_regex(
                r"(?:URLMediaFile|urlVideo_\d+)\s*=\s*url_cache\s*\+\s*'([^']+)'", webpage, 'video URL')
        video_url = prefix + video_suffix
        thumbnail_suffix = self._search_regex(
            r"(?:URLMediaStill|urlFotogramaFijo_\d+)\s*=\s*url_cache\s*\+\s*'([^']+)'",
            webpage, 'thumbnail URL', default=None)
        thumbnail = (
            None if thumbnail_suffix is None
            else prefix + thumbnail_suffix) or self._og_search_thumbnail(webpage)
        title = self._html_search_regex(
            (r"tituloVideo\s*=\s*'([^']+)'",
             r'
(.*?)
',
             r'
]+class="titulo"[^>]*>([^<]+)'),
            webpage, 'title', default=None) or self._og_search_title(webpage)
        upload_date = unified_strdate(self._search_regex(
            r'
',
            webpage, 'upload date', default=None) or self._html_search_meta(
            'datePublished', webpage, 'timestamp'))

        return {
            'id': video_id,
            'url': video_url,
            'title': title,
            'description': self._og_search_description(webpage),
            'thumbnail': thumbnail,
            'upload_date': upload_date,
        }


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

from .common import InfoExtractor
from ..compat import compat_urllib_parse_unquote

class EmbedlyIE(InfoExtractor):
    _VALID_URL = r'https?://(?:www|cdn\.)?embedly\.com/widgets/media\.html\?(?:[^#]*?&)?url=(?P<id>[^#&]+)'
    _TESTS = [{
        'url': 'https://cdn.embedly.com/widgets/media.html?src=http%3A%2F%2Fwww.youtube.com%2Fembed%2Fvideoseries%3Flist%3DUUGLim4T2loE5rwCMdpCIPVg&url=https%3A%2F%2Fwww.youtube.com%2Fwatch%3Fv%3DSU4fj_aEMVw%26list%3DUUGLim4T2loE5rwCMdpCIPVg&image=http%3A%2F%2Fi.ytimg.com%2Fvi%2FSU4fj_aEMVw%2Fhqdefault.jpg&key=8ee8a2e6a8cc47aab1a5ee67f9a178e0&type=text%2Fhtml&schema=youtube&autoplay=1',
        'only_matching': True,
    }]

    def _real_extract(self, url):
        return self.url_result(compat_urllib_parse_unquote(self._match_id(url)))


code:
from __future__ import unicode_literals

from .common import InfoExtractor

class EngadgetIE(InfoExtractor):
    _VALID_URL = r'https?://(?:www\.)?engadget\.com/video/(?P<id>[^/?#]+)'

    _TESTS = [{
        # video with 5min ID
        'url': 'http://www.engadget.com/video/518153925/',
        'md5': 'c6820d4828a5064447a4d9fc73f312c9',
        'info_dict': {
            'id': '518153925',
            'ext': 'mp4',
            'title': 'Samsung Galaxy Tab Pro 8.4 Review',
        },
        'add_ie': ['FiveMin'],
    }, {
        # video with vidible ID
        'url': 'https://www.engadget.com/video/57a28462134aa15a39f0421a/',
        'only_matching': True,
    }]

    def _real_extract(self, url):
        video_id = self._match_id(url)
        return self.url_result('aol-video:%s' % video_id)


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

import re

from .common import InfoExtractor
from ..utils import (
    encode_base_n,
    ExtractorError,
    int_or_none,
    merge_dicts,
    parse_duration,
    str_to_int,
    url_or_none,
)

class EpornerIE(InfoExtractor):
    _VALID_URL = r'https?://(?:www\.)?eporner\.com/(?:hd-porn|embed)/(?P<id>\w+)(?:/(?P<display_id>[\w-]+))?'
    _TESTS = [{
        'url': 'http://www.eporner.com/hd-porn/95008/Infamous-Tiffany-Teen-Strip-Tease-Video/',
        'md5': '39d486f046212d8e1b911c52ab4691f8',
        'info_dict': {
            'id': 'qlDUmNsj6VS',
            'display_id': 'Infamous-Tiffany-Teen-Strip-Tease-Video',
            'ext': 'mp4',
            'title': 'Infamous Tiffany Teen Strip Tease Video',
            'description': 'md5:764f39abf932daafa37485eb46efa152',
            'timestamp': 1232520922,
            'upload_date': '20090121',
            'duration': 1838,
            'view_count': int,
            'age_limit': 18,
        },
        'params': {
            'proxy': '127.0.0.1:8118'
        }
    }, {
        # New (May 2016) URL layout
        'url': 'http://www.eporner.com/hd-porn/3YRUtzMcWn0/Star-Wars-XXX-Parody/',
        'only_matching': True,
    }, {
        'url': 'http://www.eporner.com/hd-porn/3YRUtzMcWn0',
        'only_matching': True,
    }, {
        'url': 'http://www.eporner.com/hd-porn/3YRUtzMcWn0',
        'only_matching': True,
    }]

    def _real_extract(self, url):
        mobj = re.match(self._VALID_URL, url)
        video_id = mobj.group('id')
        display_id = mobj.group('display_id') or video_id

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

        video_id = self._match_id(urlh.geturl())

        hash = self._search_regex(
            r'hash\s*:\s*["\']([\da-f]{32})', webpage, 'hash')

        title = self._og_search_title(webpage, default=None) or self._html_search_regex(
            r'<title>(.+?) - EPORNER', webpage, 'title')

        # Reverse engineered from vjs.js
        def calc_hash(s):
            return ''.join((encode_base_n(int(s[lb:lb + 8], 16), 36) for lb in range(0, 32, 8)))

        video = self._download_json(
            'http://www.eporner.com/xhr/video/%s' % video_id,
            display_id, note='Downloading video JSON',
            query={
                'hash': calc_hash(hash),
                'device': 'generic',
                'domain': 'www.eporner.com',
                'fallback': 'false',
            })

        if video.get('available') is False:
            raise ExtractorError(
                '%s said: %s' % (self.IE_NAME, video['message']), expected=True)

        sources = video['sources']

        formats = []
        for kind, formats_dict in sources.items():
            if not isinstance(formats_dict, dict):
                continue
            for format_id, format_dict in formats_dict.items():
                if not isinstance(format_dict, dict):
                    continue
                src = url_or_none(format_dict.get('src'))
                if not src or not src.startswith('http'):
                    continue
                if kind == 'hls':
                    formats.extend(self._extract_m3u8_formats(
                        src, display_id, 'mp4', entry_protocol='m3u8_native',
                        m3u8_id=kind, fatal=False))
                else:
                    height = int_or_none(self._search_regex(
                        r'(\d+)[pP]', format_id, 'height', default=None))
                    fps = int_or_none(self._search_regex(
                        r'(\d+)fps', format_id, 'fps', default=None))

                    formats.append({
                        'url': src,
                        'format_id': format_id,
                        'height': height,
                        'fps': fps,
                    })
        self._sort_formats(formats)

        json_ld = self._search_json_ld(webpage, display_id, default={})

        duration = parse_duration(self._html_search_meta(
            'duration', webpage, default=None))
        view_count = str_to_int(self._search_regex(
            r'id="cinemaviews">\s*([0-9,]+)\s*<small>views',
            webpage, 'view count', fatal=False))

        return merge_dicts(json_ld, {
            'id': video_id,
            'display_id': display_id,
            'title': title,
            'duration': duration,
            'view_count': view_count,
            'formats': formats,
            'age_limit': 18,
        })


code:
from __future__ import unicode_literals

import re

from .common import InfoExtractor
from ..compat import compat_urllib_parse_urlencode
from ..utils import (
    ExtractorError,
    unescapeHTML
)

class EroProfileIE(InfoExtractor):
    _VALID_URL = r'https?://(?:www\.)?eroprofile\.com/m/videos/view/(?P<id>[^/]+)'
    _LOGIN_URL = 'http://www.eroprofile.com/auth/auth.php?'
    _NETRC_MACHINE = 'eroprofile'
    _TESTS = [{
        'url': 'http://www.eroprofile.com/m/videos/view/sexy-babe-softcore',
        'md5': 'c26f351332edf23e1ea28ce9ec9de32f',
        'info_dict': {
            'id': '3733775',
            'display_id': 'sexy-babe-softcore',
            'ext': 'm4v',
            'title': 'sexy babe softcore',
            'thumbnail': r're:https?://.*\.jpg',
            'age_limit': 18,
        }
    }, {
        'url': 'http://www.eroprofile.com/m/videos/view/Try-It-On-Pee_cut_2-wmv-4shared-com-file-sharing-download-movie-file',
        'md5': '1baa9602ede46ce904c431f5418d8916',
        'info_dict': {
            'id': '1133519',
            'ext': 'm4v',
            'title': 'Try It On Pee_cut_2.wmv - 4shared.com - file sharing - download movie file',
            'thumbnail': r're:https?://.*\.jpg',
            'age_limit': 18,
        },
        'skip': 'Requires login',
    }]

    def _login(self):
        (username, password) = self._get_login_info()
        if username is None:
            return

        query = compat_urllib_parse_urlencode({
            'username': username,
            'password': password,
            'url': 'http://www.eroprofile.com/',
        })
        login_url = self._LOGIN_URL + query
        login_page = self._download_webpage(login_url, None, False)

        m = re.search(r'Your username or password was incorrect\.', login_page)
        if m:
            raise ExtractorError(
                'Wrong username and/or password.', expected=True)

        self.report_login()
        redirect_url = self._search_regex(
            r'


code:
from __future__ import unicode_literals

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

def _decrypt_config(key, string):
    a = ''
    i = ''
    r = ''

    while len(a) < (len(string) / 2):
        a += key

    a = a[0:int(len(string) / 2)]

    t = 0
    while t < len(string):
        i += chr(int(string[t] + string[t + 1], 16))
        t += 2

    icko = [s for s in i]

    for t, c in enumerate(a):
        r += chr(ord(c) ^ ord(icko[t]))

    return r

class EscapistIE(InfoExtractor):
    _VALID_URL = r'https?://?(?:(?:www|v1)\.)?escapistmagazine\.com/videos/view/[^/]+/(?P<id>[0-9]+)'
    _TESTS = [{
        'url': 'http://www.escapistmagazine.com/videos/view/the-escapist-presents/6618-Breaking-Down-Baldurs-Gate',
        'md5': 'ab3a706c681efca53f0a35f1415cf0d1',
        'info_dict': {
            'id': '6618',
            'ext': 'mp4',
            'description': "Baldur's Gate: Original, Modded or Enhanced Edition? I'll break down what you can expect from the new Baldur's Gate: Enhanced Edition.",
            'title': "Breaking Down Baldur's Gate",
            'thumbnail': r're:^https?://.*\.jpg$',
            'duration': 264,
            'uploader': 'The Escapist',
        }
    }, {
        'url': 'http://www.escapistmagazine.com/videos/view/zero-punctuation/10044-Evolve-One-vs-Multiplayer',
        'md5': '9e8c437b0dbb0387d3bd3255ca77f6bf',
        'info_dict': {
            'id': '10044',
            'ext': 'mp4',
            'description': 'This week, Zero Punctuation reviews Evolve.',
            'title': 'Evolve - One vs Multiplayer',
            'thumbnail': r're:^https?://.*\.jpg$',
            'duration': 304,
            'uploader': 'The Escapist',
        }
    }, {
        'url': 'http://escapistmagazine.com/videos/view/the-escapist-presents/6618',
        'only_matching': True,
    }, {
        'url': 'https://v1.escapistmagazine.com/videos/view/the-escapist-presents/6618-Breaking-Down-Baldurs-Gate',
        'only_matching': True,
    }]

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

        ims_video = self._parse_json(
            self._search_regex(
                r'imsVideo\.play\(({.+?})\);', webpage, 'imsVideo'),
            video_id)
        video_id = ims_video['videoID']
        key = ims_video['hash']

        config = self._download_webpage(
            'http://www.escapistmagazine.com/videos/vidconfig.php',
            video_id, 'Downloading video config', headers={
                'Referer': url,
            }, query={
                'videoID': video_id,
                'hash': key,
            })

        data = self._parse_json(_decrypt_config(key, config), video_id)

        video_data = data['videoData']

        title = clean_html(video_data['title'])

        formats = [{
            'url': video['src'],
            'format_id': '%s-%sp' % (determine_ext(video['src']), video['res']),
            'height': int_or_none(video.get('res')),
        } for video in data['files']['videos']]
        self._sort_formats(formats)

        return {
            'id': video_id,
            'formats': formats,
            'title': title,
            'thumbnail': self._og_search_thumbnail(webpage) or data.get('poster'),
            'description': self._og_search_description(webpage),
            'duration': float_or_none(video_data.get('duration'), 1000),
            'uploader': video_data.get('publisher'),
            'series': video_data.get('show'),
        }