Exemplo n.º 1
0
_api_schema = validate.Schema({
    "error": bool,
    validate.optional("code"): validate.text,
    validate.optional("message"): validate.text,
    validate.optional("data"): object,
})
_media_schema = validate.Schema(
    {
        "stream_data":
        validate.any(
            None, {
                "streams":
                validate.all([{
                    "quality":
                    validate.any(validate.text, None),
                    "url":
                    validate.url(scheme="http",
                                 path=validate.endswith(".m3u8")),
                    validate.optional("video_encode_id"):
                    validate.text
                }])
            })
    }, validate.get("stream_data"))
_login_schema = validate.Schema({
    "auth":
    validate.text,
    "expires":
    validate.all(validate.text, validate.transform(parse_timestamp)),
    "user": {
        "username": validate.any(validate.text, None),
        "email": validate.text
Exemplo n.º 2
0
from streamlink.plugin.api.support_plugin import viasat

STREAM_API_URL = "http://playapi.mtgx.tv/v3/videos/stream/{0}"

_embed_url_re = re.compile(
    '<meta itemprop="embedURL" content="http://www.viagame.com/embed/video/([^"]+)"'
)
_store_data_re = re.compile("window.fluxData\s*=\s*JSON.parse\(\"(.+)\"\);")
_url_re = re.compile("http(s)?://(www\.)?viagame.com/channels/.+")

_store_schema = validate.Schema(
    {
        "initialStoresData": [{
            "instanceName": validate.text,
            "storeName": validate.text,
            "initialData": validate.any(dict, list)
        }]
    },
    validate.get("initialStoresData")
)
_match_store_schema = validate.Schema(
    {
        "match": {
            "id": validate.text,
            "type": validate.text,
            "videos": [{
                "id": validate.text,
                "play_id": validate.text,
            }]
        }
    },
Exemplo n.º 3
0
_param_re = re.compile(r'"param"\s*:\s*"(.+?)"\s*,\s*"time"')
_time_re = re.compile(r'"time"\s*:\s*(\d+)')
_sign_re = re.compile(r'"sign"\s*:\s*"(.+?)"')
_sd_re = re.compile(r'"SD"\s*:\s*"(\d+)"')
_hd_re = re.compile(r'"HD"\s*:\s*"(\d+)"')
_od_re = re.compile(r'"OD"\s*:\s*"(\d+)"')

_room_schema = validate.Schema(
    {
        "data": validate.any(
            validate.text,
            dict,
            {
                "videoinfo": validate.any(
                    validate.text,
                    {
                        "plflag_list": validate.text,
                        "plflag": validate.text
                    }
                )
            }
        )
    },
    validate.get("data"))


class Pandatv(Plugin):
    @classmethod
    def can_handle_url(cls, url):
        return _url_re.match(url)
Exemplo n.º 4
0
    (?:
        show/(?P<vid>[^/&?]+)|
        (?P<channel>[^/&?]+)
    )
""", re.VERBOSE)

_room_id_re = re.compile(r'"room_id\\*"\s*:\s*(\d+),')
_room_id_alt_re = re.compile(r'data-onlineid=(\d+)')

_room_id_schema = validate.Schema(
    validate.all(
        validate.transform(_room_id_re.search),
        validate.any(
            None,
            validate.all(
                validate.get(1),
                validate.transform(int)
            )
        )
    )
)

_room_id_alt_schema = validate.Schema(
    validate.all(
        validate.transform(_room_id_alt_re.search),
        validate.any(
            None,
            validate.all(
                validate.get(1),
                validate.transform(int)
            )
Exemplo n.º 5
0
BLOCK_TYPE_VIEWING_LIMIT = 1
BLOCK_TYPE_NO_SLOTS = 11

_url_re = re.compile(r"http(s)?://(\w+\.)?weeb.tv/(channel|online)/(?P<channel>[^/&?]+)")
_schema = validate.Schema(
    dict,
    validate.map(lambda k, v: (PARAMS_KEY_MAP.get(k, k), v)),
    validate.any(
        {
            "status": validate.transform(int),
            "rtmp": validate.url(scheme="rtmp"),
            "playpath": validate.text,
            "multibitrate": validate.all(
                validate.transform(int),
                validate.transform(bool)
            ),
            "block_type": validate.transform(int),
            validate.optional("token"): validate.text,
            validate.optional("block_time"): validate.text,
            validate.optional("reconnect_time"): validate.text,
        },
        {
            "status": validate.transform(int),
        },
    )
)


class Weeb(Plugin):
    @classmethod
    def can_handle_url(self, url):
        return _url_re.match(url)
Exemplo n.º 6
0
    "<embed width=\"486\" height=\"326\" flashvars=\"([^\"]+)\""
)

_live_schema = validate.Schema({
    "streams": [{
        "name": validate.text,
        "quality": validate.text,
        "url": validate.url(scheme="rtmp")
    }]
})
_schema = validate.Schema(
    validate.union({
        "export_url": validate.all(
            validate.transform(_live_export_re.search),
            validate.any(
                None,
                validate.get(1),
            )
        ),
        "video_flashvars": validate.all(
            validate.transform(_video_flashvars_re.search),
            validate.any(
                None,
                validate.all(
                    validate.get(1),
                    validate.transform(parse_query),
                    {
                        "_111pix_serverURL": validate.url(scheme="rtmp"),
                        "en_flash_providerName": validate.text
                    }
                )
            )
Exemplo n.º 7
0
    )
}

_url_re = re.compile(r"""
    http(s)?://(\w+\.)?zdf.de/
""", re.VERBOSE | re.IGNORECASE)
_api_json_re = re.compile(r'''data-zdfplayer-jsb=["'](?P<json>{.+?})["']''', re.S)

_api_schema = validate.Schema(
    validate.transform(_api_json_re.search),
    validate.any(
        None,
        validate.all(
            validate.get("json"),
            validate.transform(parse_json),
            {
                "content": validate.text,
                "apiToken": validate.text
            },
        )
    )
)

_documents_schema = validate.Schema(
    {
        "mainVideoContent": {
            "http://zdf.de/rels/target": {
                "http://zdf.de/rels/streams/ptmd": validate.text
            },
        },
    }
Exemplo n.º 8
0
class Pixiv(Plugin):
    """Plugin for https://sketch.pixiv.net/lives"""

    _url_re = re.compile(
        r"https?://sketch\.pixiv\.net/[^/]+(?P<videopage>/lives/\d+)?")

    _videopage_re = re.compile(
        r"""["']live-button["']><a\shref=["'](?P<path>[^"']+)["']""")
    _data_re = re.compile(
        r"""<script\sid=["']state["']>[^><{]+(?P<data>{[^><]+})</script>""")
    _post_key_re = re.compile(
        r"""name=["']post_key["']\svalue=["'](?P<data>[^"']+)["']""")

    _data_schema = validate.Schema(
        validate.all(
            validate.transform(_data_re.search),
            validate.any(
                None,
                validate.all(
                    validate.get("data"),
                    validate.transform(parse_json),
                    validate.get("context"),
                    validate.get("dispatcher"),
                    validate.get("stores"),
                ))))

    login_url_get = "https://accounts.pixiv.net/login"
    login_url_post = "https://accounts.pixiv.net/api/login"

    options = PluginOptions({"username": None, "password": None})

    @classmethod
    def can_handle_url(cls, url):
        return cls._url_re.match(url) is not None

    def find_videopage(self):
        self.logger.debug("Not a videopage")
        res = http.get(self.url)

        m = self._videopage_re.search(res.text)
        if not m:
            self.logger.debug(
                "No stream path, stream might be offline or invalid url.")
            raise NoStreamsError(self.url)

        path = m.group("path")
        self.logger.debug("Found new path: {0}".format(path))
        return urljoin(self.url, path)

    def _login(self, username, password):
        res = http.get(self.login_url_get)
        m = self._post_key_re.search(res.text)
        if not m:
            raise PluginError("Missing post_key, no login posible.")

        post_key = m.group("data")
        data = {
            "lang": "en",
            "source": "sketch",
            "post_key": post_key,
            "pixiv_id": username,
            "password": password,
        }

        res = http.post(self.login_url_post, data=data)
        res = http.json(res)

        if res["body"].get("success"):
            return True
        else:
            return False

    def _get_streams(self):
        http.headers = {"User-Agent": useragents.FIREFOX}

        login_username = self.get_option("username")
        login_password = self.get_option("password")
        if login_username and login_password:
            self.logger.debug("Attempting login as {0}".format(login_username))
            if self._login(login_username, login_password):
                self.logger.info(
                    "Successfully logged in as {0}".format(login_username))
            else:
                self.logger.info(
                    "Failed to login as {0}".format(login_username))

        videopage = self._url_re.match(self.url).group("videopage")
        if not videopage:
            self.url = self.find_videopage()

        data = http.get(self.url, schema=self._data_schema)

        if not data.get("LiveStore"):
            self.logger.debug("No video url found, stream might be offline.")
            return

        data = data["LiveStore"]["lives"]

        # get the unknown user-id
        for _key in data.keys():
            video_data = data.get(_key)

        owner = video_data["owner"]
        self.logger.info("Owner ID: {0}".format(owner["user_id"]))
        self.logger.debug("HLS URL: {0}".format(owner["hls_movie"]))
        for n, s in HLSStream.parse_variant_playlist(
                self.session, owner["hls_movie"]).items():
            yield n, s

        performers = video_data.get("performers")
        if performers:
            for p in performers:
                self.logger.info("CO-HOST ID: {0}".format(p["user_id"]))
                hls_url = p["hls_movie"]
                self.logger.debug("HLS URL: {0}".format(hls_url))
                for n, s in HLSStream.parse_variant_playlist(
                        self.session, hls_url).items():
                    _n = "{0}_{1}".format(n, p["user_id"])
                    yield _n, s
Exemplo n.º 9
0
class App17(Plugin):
    _url_re = re.compile(r"https://17.live/live/(?P<channel>[^/&?]+)")
    API_URL = "https://api-dsa.17app.co/api/v1/lives/{0}/viewers/alive"

    _api_schema = validate.Schema(
        {
            "rtmpUrls": [{
                validate.optional("provider"): validate.any(int, None),
                "url": validate.url(),
            }],
        },
        validate.get("rtmpUrls"),
    )

    @classmethod
    def can_handle_url(cls, url):
        return cls._url_re.match(url) is not None

    def _get_streams(self):
        match = self._url_re.match(self.url)
        channel = match.group("channel")

        self.session.http.headers.update({
            'User-Agent': useragents.CHROME,
            'Referer': self.url
        })

        data = '{"liveStreamID":"%s"}' % (channel)

        try:
            res = self.session.http.post(self.API_URL.format(channel),
                                         data=data)
            res_json = self.session.http.json(res, schema=self._api_schema)
            log.trace("{0!r}".format(res_json))
            http_url = res_json[0]["url"]
        except Exception as e:
            log.info("Stream currently unavailable.")
            log.debug(str(e))
            return

        https_url = http_url.replace("http:", "https:")
        yield "live", HTTPStream(self.session, https_url)

        if 'pull-rtmp' in http_url:
            rtmp_url = http_url.replace("http:", "rtmp:").replace(".flv", "")
            stream = RTMPStream(self.session, {
                "rtmp": rtmp_url,
                "live": True,
                "pageUrl": self.url,
            })
            yield "live", stream

        if 'wansu-' in http_url:
            hls_url = http_url.replace(".flv", "/playlist.m3u8")
        else:
            hls_url = http_url.replace("live-hdl",
                                       "live-hls").replace(".flv", ".m3u8")

        s = HLSStream.parse_variant_playlist(self.session, hls_url)
        if not s:
            yield "live", HLSStream(self.session, hls_url)
        else:
            if len(s) == 1:
                for _n, _s in s.items():
                    yield "live", _s
            else:
                yield from s.items()
Exemplo n.º 10
0
    https?://(?:\w+\.)?arte\.tv/(?:guide/)?
    (?P<language>[a-z]{2})/
    (?:
        (?:videos/)?(?P<video_id>(?!RC\-|videos)[^/]+?)/.+ | # VOD
        (?:direct|live)        # Live TV
    )
""", re.VERBOSE)

_video_schema = validate.Schema({
    "videoJsonPlayer": {
        "VSR": validate.any(
            [],
            {
                validate.text: {
                    "height": int,
                    "mediaType": validate.text,
                    "url": validate.text,
                    "versionShortLibelle": validate.text
                },
            },
        )
    }
})


class ArteTV(Plugin):
    @classmethod
    def can_handle_url(cls, url):
        return _url_re.match(url)

    def _create_stream(self, stream, language):
Exemplo n.º 11
0
_media_inner_schema = validate.Schema([{
    "layerList": [{
        "name":
        validate.text,
        validate.optional("sequenceList"): [{
            "layerList":
            validate.all([{
                "name": validate.text,
                validate.optional("param"): dict
            }], validate.filter(lambda l: l["name"] in ("video", "reporting")))
        }]
    }]
}])
_media_schema = validate.Schema(
    validate.any(
        _media_inner_schema,
        validate.all({"sequence": _media_inner_schema},
                     validate.get("sequence"))))
_vod_playlist_schema = validate.Schema({
    "duration": float,
    "fragments": [[int, float]],
    "template": validate.text
})
_vod_manifest_schema = validate.Schema({
    "alternates": [{
        "height": int,
        "template": validate.text,
        validate.optional("failover"): [validate.text]
    }]
})

Exemplo n.º 12
0
CHANNEL_INFO_URL = "http://api.plu.cn/tga/streams/%s"
QQ_STREAM_INFO_URL = "http://info.zb.qq.com/?cnlid=%d&cmd=2&stream=%d&system=1&sdtfrom=113"
PLU_STREAM_INFO_URL = "http://star.api.plu.cn/live/GetLiveUrl?roomId=%d"

_quality_re = re.compile(r"\d+x(\d+)$")
_url_re = re.compile(
    r"http://star\.longzhu\.(?:tv|com)/(m\/)?(?P<domain>[a-z0-9]+)")

_channel_schema = validate.Schema(
    {
        "data":
        validate.any(
            None, {
                "channel":
                validate.any(
                    None, {
                        "id": validate.all(validate.text,
                                           validate.transform(int)),
                        "vid": int
                    })
            })
    }, validate.get("data"))

_plu_schema = validate.Schema({
    "urls": [{
        "securityUrl": validate.url(scheme=validate.any("rtmp", "http")),
        "resolution": validate.text,
        "ext": validate.text
    }]
})

_qq_schema = validate.Schema(
Exemplo n.º 13
0
API_URL = "https://api.live.bilibili.com/room/v1/Room/playUrl"
ROOM_API = "https://api.live.bilibili.com/room/v1/Room/room_init?id={}"
SHOW_STATUS_OFFLINE = 0
SHOW_STATUS_ONLINE = 1
SHOW_STATUS_ROUND = 2
STREAM_WEIGHTS = {"source": 1080}

_url_re = re.compile(
    r"""
    http(s)?://live.bilibili.com
    /(?P<channel>[^/]+)
""", re.VERBOSE)

_room_id_schema = validate.Schema(
    {"data": validate.any(None, {
        "room_id": int,
        "live_status": int
    })}, validate.get("data"))

_room_stream_list_schema = validate.Schema(
    {"data": validate.any(None, {"durl": [{
        "url": validate.url()
    }]})}, validate.get("data"))


class Bilibili(Plugin):
    @classmethod
    def can_handle_url(self, url):
        return _url_re.match(url)

    @classmethod
    def stream_weight(cls, stream):
Exemplo n.º 14
0
class RUtube(Plugin):
    ''' https://rutube.ru/feeds/live/ '''

    api_play = 'https://rutube.ru/api/play/options/{0}/?format=json&no_404=true&referer={1}'

    _url_re = re.compile(
        r'''https?://(\w+\.)?rutube\.ru/(?:play|video)/(?:embed/)?(?P<id>[a-z0-9]+)'''
    )

    _video_schema = validate.Schema(
        validate.any(
            {
                'live_streams': {
                    validate.text: [{
                        'url': validate.text,
                    }]
                },
                'video_balancer': {
                    validate.text: validate.text,
                },
            }, {}))

    @classmethod
    def can_handle_url(cls, url):
        return cls._url_re.match(url) is not None

    def _get_streams(self):
        log.debug('Version 2018-07-01')
        log.info('This is a custom plugin. '
                 'For support visit https://github.com/back-to/plugins')
        hls_urls = []
        hds_urls = []

        http.headers.update({'User-Agent': useragents.FIREFOX})

        match = self._url_re.match(self.url)
        if match is None:
            return

        video_id = match.group('id')
        log.debug('video_id: {0}'.format(video_id))

        res = http.get(self.api_play.format(video_id, self.url))
        data = http.json(res, schema=self._video_schema)

        live_data = data.get('live_streams')
        vod_data = data.get('video_balancer')

        if live_data:
            log.debug('Found live_data')
            for d in live_data['hls']:
                hls_urls.append(d['url'])
            for e in live_data['hds']:
                hds_urls.append(e['url'])
        elif vod_data:
            log.debug('Found vod_data')
            hls_urls.append(vod_data['m3u8'])
            hds_urls.append(vod_data['default'])
        else:
            log.error('This video is not available in your region.')
            raise NoStreamsError(self.url)

        for hls_url in hls_urls:
            log.debug('HLS URL: {0}'.format(hls_url))
            for s in HLSStream.parse_variant_playlist(self.session,
                                                      hls_url).items():
                yield s

        for hds_url in hds_urls:
            log.debug('HDS URL: {0}'.format(hds_url))
            for s in HDSStream.parse_manifest(self.session, hds_url).items():
                yield s
Exemplo n.º 15
0
_room_id_re = re.compile(r'"roomId":(?P<room_id>\d+),')
_room_id_alt_re = re.compile(r'content="showroom:///room\?room_id=(?P<room_id>\d+)"')
_room_id_lookup_failure_log = 'Failed to find room_id for {0} using {1} regex'

_api_status_url = 'https://www.showroom-live.com/room/is_live?room_id={room_id}'
_api_stream_url = 'https://www.showroom-live.com/api/live/streaming_url?room_id={room_id}'

_api_stream_schema = validate.Schema(
    validate.any({
        "streaming_url_list": validate.all([
            {
                "url": validate.text,
                validate.optional("stream_name"): validate.text,
                "id": int,
                "label": validate.text,
                "is_default": int,
                "type": validate.text,
                "quality": int,
            }
        ])
    },
        {}
    )
)

# the "low latency" streams are rtmp, the others are hls
_rtmp_quality_lookup = {
    "オリジナル画質": "high",
    "オリジナル画質(低遅延)": "high",
    "original spec(low latency)": "high",
    "original spec": "high",
Exemplo n.º 16
0
_swf_url_re = re.compile(r"swfobject.embedSWF\(\"([^\"]+)\",")

_schema = validate.Schema(
    validate.union({
        "urls": validate.all(
            validate.transform(_file_re.findall),
            validate.map(unquote),
            [validate.url()]
        ),
        "swf": validate.all(
            validate.transform(_swf_url_re.search),
            validate.any(
                None,
                validate.all(
                    validate.get(1),
                    validate.url(
                        scheme="http",
                        path=validate.endswith("swf")
                    )
                )
            )
        )
    })
)


class Aliez(Plugin):
    @classmethod
    def can_handle_url(self, url):
        return _url_re.match(url)

    def _get_streams(self):
Exemplo n.º 17
0
import random
import re

from streamlink.plugin import Plugin
from streamlink.plugin.api import http
from streamlink.plugin.api import validate
from streamlink.plugin.api import useragents
from streamlink.stream import HLSStream

_url_re = re.compile(r"http(s)?://(www\.)?camsoda\.com/(?P<username>[^\"\']+)")

_api_user_schema = validate.Schema(
    {
        "status": validate.any(int, validate.text),
        validate.optional("user"): {
            "online": validate.any(int, validate.text),
            "chatstatus": validate.text,
        }
    }
)

_api_video_schema = validate.Schema(
    {
        "token": validate.text,
        "app": validate.text,
        "edge_servers": [validate.text],
        "stream_name": validate.text
    }
)

Exemplo n.º 18
0
class SBScokr(Plugin):

    api_channel = 'http://apis.sbs.co.kr/play-api/1.0/onair/channel/{0}'
    api_channels = 'http://static.apis.sbs.co.kr/play-api/1.0/onair/channels'

    _url_re = re.compile(r'https?://play\.sbs\.co\.kr/onair/pc/index.html')

    _channels_schema = validate.Schema({
        'list': [{
            'channelname': validate.all(
                validate.text,
            ),
            'channelid': validate.text,
            validate.optional('type'): validate.text,
        }]},
        validate.get('list'),
    )

    _channel_schema = validate.Schema(
        {
            'onair': {
                'info': {
                    'onair_yn': validate.text,
                    'overseas_yn': validate.text,
                    'overseas_text': validate.text,
                },
                'source': {
                    'mediasourcelist': validate.any([{
                        validate.optional('default'): validate.text,
                        'mediaurl': validate.text,
                    }], [])
                },
            }
        },
        validate.get('onair'),
    )

    arguments = PluginArguments(
        PluginArgument(
            'id',
            metavar='CHANNELID',
            type=str.upper,
            help='''
            Channel ID to play.

            Example:

                %(prog)s http://play.sbs.co.kr/onair/pc/index.html best --sbscokr-id S01

            '''
        )
    )

    @classmethod
    def can_handle_url(cls, url):
        return cls._url_re.match(url) is not None

    def _get_streams(self):
        user_channel_id = self.get_option('id')

        res = self.session.http.get(self.api_channels)
        res = self.session.http.json(res, schema=self._channels_schema)

        channels = {}
        for channel in sorted(res, key=lambda x: x['channelid']):
            if channel.get('type') in ('TV', 'Radio'):
                channels[channel['channelid']] = channel['channelname']

        log.info('Available IDs: {0}'.format(', '.join(
            '{0} ({1})'.format(key, value) for key, value in channels.items())))
        if not user_channel_id:
            log.error('No channel selected, use --sbscokr-id CHANNELID')
            return
        elif user_channel_id and user_channel_id not in channels.keys():
            log.error('Channel ID "{0}" is not available.'.format(user_channel_id))
            return

        params = {
            'v_type': '2',
            'platform': 'pcweb',
            'protocol': 'hls',
            'jwt-token': '',
            'rnd': random.randint(50, 300)
        }

        res = self.session.http.get(self.api_channel.format(user_channel_id),
                                    params=params)
        res = self.session.http.json(res, schema=self._channel_schema)

        for media in res['source']['mediasourcelist']:
            if media['mediaurl']:
                for s in HLSStream.parse_variant_playlist(self.session,
                                                          media['mediaurl']).items():
                    yield s
        else:
            if res['info']['onair_yn'] != 'Y':
                log.error('This channel is currently unavailable')
            elif res['info']['overseas_yn'] != 'Y':
                log.error(res['info']['overseas_text'])
Exemplo n.º 19
0
            "media_is_live": validate.all(
                validate.text,
                validate.transform(int),
                validate.transform(bool)
            ),
            "media_id": validate.text
        }],
    },
    validate.get("livestream"),
    validate.length(1),
    validate.get(0)
)
_player_schema = validate.Schema(
    {
        "clip": {
            "baseUrl": validate.any(None, validate.text),
            "bitrates": validate.all(
                validate.filter(lambda b: b.get("url") and b.get("label")),
                [{
                    "label": validate.text,
                    "url": validate.text,
                }],
            )
        },
        validate.optional("playlist"): [{
            validate.optional("connectionProvider"): validate.text,
            validate.optional("netConnectionUrl"): validate.text,
            validate.optional("bitrates"): [{
                "label": validate.text,
                "url": validate.text,
                "provider": validate.text
Exemplo n.º 20
0
class DLive(Plugin):
    _re_url = re.compile(
        r"""
        https?://(?:www\.)?dlive\.tv/
        (?:
            (?:p/(?P<video>[^/]+))
            |
            (?P<channel>[^/]+)
        )
    """, re.VERBOSE)
    _re_videoPlaybackUrl = re.compile(r'"playbackUrl"\s*:\s*"([^"]+\.m3u8)"')

    _schema_userByDisplayName = validate.Schema(
        {
            "data": {
                "userByDisplayName": {
                    "livestream": validate.any(None, {"title": validate.text}),
                    "username": validate.text
                }
            }
        }, validate.get("data"), validate.get("userByDisplayName"))
    _schema_videoPlaybackUrl = validate.Schema(
        validate.transform(_re_videoPlaybackUrl.search),
        validate.any(
            None,
            validate.all(
                validate.get(1), validate.transform(unquote_plus),
                validate.transform(
                    lambda url: bytes(url, "utf-8").decode("unicode_escape")),
                validate.url())))

    @classmethod
    def can_handle_url(cls, url):
        return cls._re_url.match(url)

    @classmethod
    def stream_weight(cls, key):
        weight = QUALITY_WEIGHTS.get(key)
        if weight:
            return weight, "dlive"

        return Plugin.stream_weight(key)

    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        self.author = None
        self.title = None

        match = self._re_url.match(self.url)
        self.video = match.group("video")
        self.channel = match.group("channel")

    def get_author(self):
        return self.author

    def get_title(self):
        return self.title

    def _get_streams_video(self):
        log.debug("Getting video HLS streams for {0}".format(self.video))
        try:
            hls_url = self.session.http.get(
                self.url, schema=self._schema_videoPlaybackUrl)
            if hls_url is None:
                return
        except PluginError:
            return

        return HLSStream.parse_variant_playlist(self.session, hls_url)

    def _get_streams_live(self):
        log.debug("Getting live HLS streams for {0}".format(self.channel))
        try:
            data = json.dumps({
                "query":
                """query {{
                userByDisplayName(displayname:"{displayname}") {{
                    livestream {{
                        title
                    }}
                    username
                }}
            }}""".format(displayname=self.channel)
            })
            res = self.session.http.post("https://graphigo.prd.dlive.tv/",
                                         data=data)
            res = self.session.http.json(res,
                                         schema=self._schema_userByDisplayName)
            if res["livestream"] is None:
                return
        except PluginError:
            return

        self.author = self.channel
        self.title = res["livestream"]["title"]

        hls_url = "https://live.prd.dlive.tv/hls/live/{0}.m3u8".format(
            res["username"])

        return HLSStream.parse_variant_playlist(self.session, hls_url)

    def _get_streams(self):
        if self.video:
            return self._get_streams_video()
        elif self.channel:
            return self._get_streams_live()
Exemplo n.º 21
0
API_SECRET = "95acd7f6cc3392f3"
SHOW_STATUS_ONLINE = 1
SHOW_STATUS_OFFLINE = 2
STREAM_WEIGHTS = {
    "source": 1080
}

_url_re = re.compile(r"""
    http(s)?://live.bilibili.com
    /(?P<channel>[^/]+)
""", re.VERBOSE)

_room_id_schema = validate.Schema(
    {
        "data": validate.any(None, {
            "room_id": int
        })
    },
    validate.get("data")
)


class Bilibili(Plugin):
    @classmethod
    def can_handle_url(self, url):
        return _url_re.match(url)

    @classmethod
    def stream_weight(cls, stream):
        if stream in STREAM_WEIGHTS:
            return STREAM_WEIGHTS[stream], "Bilibili"
Exemplo n.º 22
0
class Pluzz(Plugin):
    GEO_URL = 'http://geo.francetv.fr/ws/edgescape.json'
    API_URL = 'http://sivideo.webservices.francetelevisions.fr/tools/getInfosOeuvre/v2/?idDiffusion={0}'
    PLAYER_GENERATOR_URL = 'https://sivideo.webservices.francetelevisions.fr/assets/staticmd5/getUrl?id=jquery.player.7.js'
    TOKEN_URL = 'http://hdfauthftv-a.akamaihd.net/esi/TA?url={0}'

    _url_re = re.compile(r'https?://((?:www)\.france\.tv/.+\.html|www\.(ludo|zouzous)\.fr/heros/[\w-]+|(sport|france3-regions)\.francetvinfo\.fr/.+?/(tv/direct)?)')
    _pluzz_video_id_re = re.compile(r'data-main-video="(?P<video_id>.+?)"')
    _jeunesse_video_id_re = re.compile(r'playlist: \[{.*?,"identity":"(?P<video_id>.+?)@(?P<catalogue>Ludo|Zouzous)"')
    _f3_regions_video_id_re = re.compile(r'"http://videos\.francetv\.fr/video/(?P<video_id>.+?)@Regions"')
    _sport_video_id_re = re.compile(r'data-video="(?P<video_id>.+?)"')
    _player_re = re.compile(r'src="(?P<player>//staticftv-a\.akamaihd\.net/player/jquery\.player.+?-[0-9a-f]+?\.js)"></script>')
    _swf_re = re.compile(r'//staticftv-a\.akamaihd\.net/player/bower_components/player_flash/dist/FranceTVNVPVFlashPlayer\.akamai-[0-9a-f]+\.swf')
    _hds_pv_data_re = re.compile(r"~data=.+?!")
    _mp4_bitrate_re = re.compile(r'.*-(?P<bitrate>[0-9]+k)\.mp4')

    _geo_schema = validate.Schema({
        'reponse': {
            'geo_info': {
                'country_code': validate.text
            }
        }
    })

    _api_schema = validate.Schema({
        'videos': validate.all(
            [{
                'format': validate.any(
                    None,
                    validate.text
                ),
                'url': validate.any(
                    None,
                    validate.url(),
                ),
                'statut': validate.text,
                'drm': bool,
                'geoblocage': validate.any(
                    None,
                    [validate.all(validate.text)]
                ),
                'plages_ouverture': validate.all(
                    [{
                        'debut': validate.any(
                            None,
                            int
                        ),
                        'fin': validate.any(
                            None,
                            int
                        )
                    }]
                )
            }]
        ),
        'subtitles': validate.any(
            [],
            validate.all(
                [{
                    'type': validate.text,
                    'url': validate.url(),
                    'format': validate.text
                }]
            )
        )
    })

    _player_schema = validate.Schema({'result': validate.url()})

    options = PluginOptions({
        "mux_subtitles": False
    })

    @classmethod
    def can_handle_url(cls, url):
        return Pluzz._url_re.match(url)

    def _get_streams(self):
        # Retrieve geolocation data
        res = http.get(self.GEO_URL)
        geo = http.json(res, schema=self._geo_schema)
        country_code = geo['reponse']['geo_info']['country_code']

        # Retrieve URL page and search for video ID
        res = http.get(self.url)
        if 'france.tv' in self.url:
            match = self._pluzz_video_id_re.search(res.text)
        elif 'ludo.fr' in self.url or 'zouzous.fr' in self.url:
            match = self._jeunesse_video_id_re.search(res.text)
        elif 'france3-regions.francetvinfo.fr' in self.url:
            match = self._f3_regions_video_id_re.search(res.text)
        elif 'sport.francetvinfo.fr' in self.url:
            match = self._sport_video_id_re.search(res.text)
        if match is None:
            return
        video_id = match.group('video_id')

        # Retrieve SWF player URL
        swf_url = None
        res = http.get(self.PLAYER_GENERATOR_URL)
        player_url = update_scheme(self.url, http.json(res, schema=self._player_schema)['result'])
        res = http.get(player_url)
        match = self._swf_re.search(res.text)
        if match is not None:
            swf_url = update_scheme(self.url, match.group(0))

        res = http.get(self.API_URL.format(video_id))
        videos = http.json(res, schema=self._api_schema)
        now = time.time()

        offline = False
        geolocked = False
        drm = False
        expired = False

        streams = []
        for video in videos['videos']:
            video_url = video['url']

            # Check whether video format is available
            if video['statut'] != 'ONLINE':
                offline = offline or True
                continue

            # Check whether video format is geo-locked
            if video['geoblocage'] is not None and country_code not in video['geoblocage']:
                geolocked = geolocked or True
                continue

            # Check whether video is DRM-protected
            if video['drm']:
                drm = drm or True
                continue

            # Check whether video format is expired
            available = False
            for interval in video['plages_ouverture']:
                available = (interval['debut'] or 0) <= now <= (interval['fin'] or sys.maxsize)
                if available:
                    break
            if not available:
                expired = expired or True
                continue

            # TODO: add DASH streams once supported
            if '.mpd' in video_url:
                continue

            if '.f4m' in video_url or 'france.tv' in self.url:
                res = http.get(self.TOKEN_URL.format(video_url))
                video_url = res.text

            if '.f4m' in video_url and swf_url is not None:
                for bitrate, stream in HDSStream.parse_manifest(self.session,
                                                                video_url,
                                                                is_akamai=True,
                                                                pvswf=swf_url).items():
                    # HDS videos with data in their manifest fragment token
                    # doesn't seem to be supported by HDSStream. Ignore such
                    # stream (but HDS stream having only the hdntl parameter in
                    # their manifest token will be provided)
                    pvtoken = stream.request_params['params'].get('pvtoken', '')
                    match = self._hds_pv_data_re.search(pvtoken)
                    if match is None:
                        streams.append((bitrate, stream))
            elif '.m3u8' in video_url:
                for stream in HLSStream.parse_variant_playlist(self.session, video_url).items():
                    streams.append(stream)
            # HBB TV streams are not provided anymore by France Televisions
            elif '.mp4' in video_url and '/hbbtv/' not in video_url:
                match = self._mp4_bitrate_re.match(video_url)
                if match is not None:
                    bitrate = match.group('bitrate')
                else:
                    # Fallback bitrate (seems all France Televisions MP4 videos
                    # seem have such bitrate)
                    bitrate = '1500k'
                streams.append((bitrate, HTTPStream(self.session, video_url)))

        if self.get_option("mux_subtitles") and videos['subtitles'] != []:
            substreams = {}
            for subtitle in videos['subtitles']:
                # TTML subtitles are available but not supported by FFmpeg
                if subtitle['format'] == 'ttml':
                    continue
                substreams[subtitle['type']] = HTTPStream(self.session, subtitle['url'])

            for quality, stream in streams:
                yield quality, MuxedStream(self.session, stream, subtitles=substreams)
        else:
            for stream in streams:
                yield stream

        if offline:
            self.logger.error('Failed to access stream, may be due to offline content')
        if geolocked:
            self.logger.error('Failed to access stream, may be due to geo-restricted content')
        if drm:
            self.logger.error('Failed to access stream, may be due to DRM-protected content')
        if expired:
            self.logger.error('Failed to access stream, may be due to expired content')
Exemplo n.º 23
0
        validate.optional("sequenceList"): [{
            "layerList": validate.all(
                [{
                    "name": validate.text,
                    validate.optional("param"): dict
                }],
                validate.filter(lambda l: l["name"] in ("video", "reporting"))
            )
        }]
    }]
}])
_media_schema = validate.Schema(
    validate.any(
        _media_inner_schema,
        validate.all(
            {"sequence": _media_inner_schema},
            validate.get("sequence")
        )
    )
)
_vod_playlist_schema = validate.Schema({
    "duration": float,
    "fragments": [[int, float]],
    "template": validate.text
})
_vod_manifest_schema = validate.Schema({
    "alternates": [{
        "height": int,
        "template": validate.text,
        validate.optional("failover"): [validate.text]
    }]
Exemplo n.º 24
0
class Reuters(Plugin):
    _url_re = re.compile(r'https?://(.*?\.)?reuters\.(com|tv)')
    _id_re = re.compile(r'(/l/|id=)(?P<id>.*?)(/|\?|$)')
    _iframe_url = 'https://www.reuters.tv/l/{0}/?nonav=true'
    _hls_re = re.compile(r'''(?<!')https://[^"';!<>]+\.m3u8''')
    _json_re = re.compile(r'''(?P<data>{.*});''')
    _data_schema = validate.Schema(
        validate.transform(_json_re.search),
        validate.any(
            None,
            validate.all(
                validate.get('data'), validate.transform(parse_json), {
                    'title':
                    validate.text,
                    'items': [{
                        'title':
                        validate.text,
                        'type':
                        validate.text,
                        'resources':
                        [{
                            'mimeType': validate.text,
                            'uri': validate.url(),
                            validate.optional('protocol'): validate.text,
                            validate.optional('entityType'): validate.text,
                        }]
                    }],
                })))

    def __init__(self, url):
        super(Reuters, self).__init__(url)
        self.title = None

    @classmethod
    def can_handle_url(cls, url):
        return cls._url_re.match(url) is not None

    def get_title(self):
        if not self.title:
            self._get_data()
        return self.title

    def _get_data(self):
        res = self.session.http.get(self.url)
        for script in itertags(res.text, 'script'):
            if script.attributes.get(
                    'type'
            ) == 'text/javascript' and '#rtvIframe' in script.text:
                m = self._id_re.search(self.url)
                if m and m.group('id'):
                    log.debug('ID: {0}'.format(m.group('id')))
                    res = self.session.http.get(
                        self._iframe_url.format(m.group('id')))

        for script in itertags(res.text, 'script'):
            if script.attributes.get(
                    'type') == 'text/javascript' and 'RTVJson' in script.text:
                data = self._data_schema.validate(script.text)
                if not data:
                    continue
                self.title = data['title']
                for item in data['items']:
                    if data['title'] == item['title']:
                        log.trace('{0!r}'.format(item))
                        log.debug('Type: {0}'.format(item['type']))
                        for res in item['resources']:
                            if res['mimeType'] == 'application/x-mpegURL':
                                return res['uri']

        # fallback
        for title in itertags(res.text, 'title'):
            self.title = title.text
        m = self._hls_re.search(res.text)
        if not m:
            log.error('Unsupported PageType.')
            return
        return m.group(0)

    def _get_streams(self):
        hls_url = self._get_data()
        if not hls_url:
            return
        log.debug('URL={0}'.format(hls_url))
        return HLSStream.parse_variant_playlist(self.session, hls_url)
Exemplo n.º 25
0
from streamlink.compat import urljoin
from streamlink.plugin import Plugin
from streamlink.plugin.api import http, validate
from streamlink.plugin.api.utils import parse_json
from streamlink.stream import AkamaiHDStream, HLSStream

_url_re = re.compile(r"http(s)?://(www\.)?livestream.com/")
_stream_config_schema = validate.Schema({
    "event": {
        "stream_info": validate.any({
            "is_live": bool,
            "qualities": [{
                "bitrate": int,
                "height": int
            }],
            validate.optional("play_url"): validate.url(scheme="http"),
            validate.optional("m3u8_url"): validate.url(
                scheme="http",
                path=validate.endswith(".m3u8")
            ),
        }, None)
    },
    validate.optional("playerUri"): validate.text,
    validate.optional("viewerPlusSwfUrl"): validate.url(scheme="http"),
    validate.optional("lsPlayerSwfUrl"): validate.text,
    validate.optional("hdPlayerSwfUrl"): validate.text
})
_smil_schema = validate.Schema(validate.union({
    "http_base": validate.all(
        validate.xml_find("{http://www.w3.org/2001/SMIL20/Language}head/"
                          "{http://www.w3.org/2001/SMIL20/Language}meta"
Exemplo n.º 26
0
class Picarto(Plugin):
    url_re = re.compile(
        r'''
        https?://(?:www\.)?picarto\.tv/
            (?:(?P<po>streampopout|videopopout)/)?
            (?P<user>[^&?/]+)
            (?:\?tab=videos&id=(?P<vod_id>\d+))?
    ''', re.VERBOSE)

    channel_schema = validate.Schema({
        'channel':
        validate.any(
            None, {
                'stream_name': validate.text,
                'title': validate.text,
                'online': bool,
                'private': bool,
                'categories': [{
                    'label': validate.text
                }],
            }),
        'getLoadBalancerUrl': {
            'origin': validate.text
        },
        'getMultiStreams':
        validate.any(
            None, {
                'multistream': bool,
                'streams': [{
                    'name': validate.text,
                    'online': bool,
                }],
            }),
    })
    vod_schema = validate.Schema(
        {
            'data': {
                'video':
                validate.any(
                    None, {
                        'id': validate.text,
                        'title': validate.text,
                        'file_name': validate.text,
                        'channel': {
                            'name': validate.text
                        },
                    }),
            },
        }, validate.get('data'), validate.get('video'))

    HLS_URL = 'https://{origin}.picarto.tv/stream/hls/{file_name}/index.m3u8'

    author = None
    category = None
    title = None

    @classmethod
    def can_handle_url(cls, url):
        return cls.url_re.match(url) is not None

    def get_author(self):
        return self.author

    def get_category(self):
        return self.category

    def get_title(self):
        return self.title

    def get_live(self, username):
        res = self.session.http.get(
            'https://ptvintern.picarto.tv/api/channel/detail/{0}'.format(
                username))
        channel_data = self.session.http.json(res, schema=self.channel_schema)
        log.trace('channel_data={0!r}'.format(channel_data))

        if not channel_data['channel'] or not channel_data['getMultiStreams']:
            log.debug('Missing channel or streaming data')
            return

        if channel_data['channel']['private']:
            log.info('This is a private stream')
            return

        if channel_data['getMultiStreams']['multistream']:
            msg = 'Found multistream: '
            i = 1
            for user in channel_data['getMultiStreams']['streams']:
                msg += user['name']
                msg += ' (online)' if user['online'] else ' (offline)'
                if i < len(channel_data['getMultiStreams']['streams']):
                    msg += ', '
                i += 1
            log.info(msg)

        if not channel_data['channel']['online']:
            log.error('User is not online')
            return

        self.author = username
        self.category = channel_data['channel']['categories'][0]['label']
        self.title = channel_data['channel']['title']

        return HLSStream.parse_variant_playlist(
            self.session,
            self.HLS_URL.format(
                file_name=channel_data['channel']['stream_name'],
                origin=channel_data['getLoadBalancerUrl']['origin']))

    def get_vod(self, vod_id):
        data = {
            'query': ('query ($videoId: ID!) {\n'
                      '  video(id: $videoId) {\n'
                      '    id\n'
                      '    title\n'
                      '    file_name\n'
                      '    channel {\n'
                      '      name\n'
                      '      }'
                      '  }\n'
                      '}\n'),
            'variables': {
                'videoId': vod_id
            },
        }
        res = self.session.http.post('https://ptvintern.picarto.tv/ptvapi',
                                     json=data)
        vod_data = self.session.http.json(res, schema=self.vod_schema)
        log.trace('vod_data={0!r}'.format(vod_data))
        if not vod_data:
            log.debug('Missing video data')
            return

        self.author = vod_data['channel']['name']
        self.category = 'VOD'
        self.title = vod_data['title']
        return HLSStream.parse_variant_playlist(
            self.session,
            self.HLS_URL.format(file_name=vod_data['file_name'],
                                origin='recording-eu-1'))

    def _get_streams(self):
        m = self.url_re.match(self.url).groupdict()

        if (m['po'] == 'streampopout'
                or not m['po']) and m['user'] and not m['vod_id']:
            log.debug('Type=Live')
            return self.get_live(m['user'])
        elif m['po'] == 'videopopout' or (m['user'] and m['vod_id']):
            log.debug('Type=VOD')
            vod_id = m['vod_id'] if m['vod_id'] else m['user']
            return self.get_vod(vod_id)
Exemplo n.º 27
0
        "CHANNEL": {
            "RESULT": validate.transform(int),
            validate.optional("BPWD"): validate.text,
            validate.optional("BNO"): validate.text,
            validate.optional("RMD"): validate.text,
            validate.optional("AID"): validate.text,
            validate.optional("CDN"): validate.text
        }
    },
    validate.get("CHANNEL")
)

_stream_schema = validate.Schema(
    {
        validate.optional("view_url"): validate.url(
            scheme=validate.any("rtmp", "http")
        ),
        "stream_status": validate.text
    }
)


class AfreecaTV(Plugin):
    login_url = "https://member.afreecatv.com:8111/login/LoginAction.php"

    arguments = PluginArguments(
        PluginArgument(
            "username",
            requires=["password"],
            metavar="USERNAME",
            help="The username used to register with afreecatv.com."
Exemplo n.º 28
0
class Mjunoon(Plugin):
    login_url = 'https://cdn2.mjunoon.tv:9191/v2/auth/login'
    stream_url = 'https://cdn2.mjunoon.tv:9191/v2/streaming-url'

    is_live_channel_re = re.compile(r'"isLiveBroadcast":\s*"(true|undefined)"')

    main_chunk_js_url_re = re.compile(
        r'<script src="(/static/js/main\.\w+\.chunk\.js)"></script>')

    js_credentials_re = re.compile(
        r'data:{email:"(?P<email>.*?)",password:"******"}')

    js_cipher_data_re = re.compile(
        r'createDecipheriv\("(?P<algorithm>.*?)","(?P<key>.*?)","(?P<iv>.*?)"\)'
    )

    token_schema = validate.Schema({
        'token': str,
        'token_type': str,
        'expires_in': int,
    })

    encrypted_data_schema = validate.Schema({
        'eData': str,
    }, validate.get('eData'))

    stream_schema = validate.Schema(
        {
            'data': {
                'live_stream_url':
                validate.url(),
                'channel_name':
                str,
                'meta_title':
                validate.any(None, str),
                'genres':
                validate.all(
                    validate.transform(lambda x: x.split(',')[0]),
                    str,
                ),
            },
        }, validate.get('data'))

    encryption_algorithm = {
        'aes-256-cbc': AES.MODE_CBC,
    }

    def get_data(self):
        js_data = {}
        res = self.session.http.get(self.url)

        m = self.is_live_channel_re.search(res.text)
        if not m:
            return

        if m.group(1) == "true":
            js_data['type'] = 'channel'
        else:
            js_data['type'] = 'episode'

        m = self.main_chunk_js_url_re.search(res.text)
        if not m:
            log.error('Failed to get main chunk JS URL')
            return

        res = self.session.http.get(urljoin(self.url, m.group(1)))

        m = self.js_credentials_re.search(res.text)
        if not m:
            log.error('Failed to get credentials')
            return

        js_data['credentials'] = m.groupdict()

        m = self.js_cipher_data_re.search(res.text)
        if not m:
            log.error('Failed to get cipher data')
            return

        js_data['cipher_data'] = m.groupdict()

        return js_data

    def decrypt_data(self, cipher_data, encrypted_data):
        cipher = AES.new(
            bytes(cipher_data['key'], 'utf-8'),
            self.encryption_algorithm[cipher_data['algorithm']],
            bytes(cipher_data['iv'], 'utf-8'),
        )

        return unpad(cipher.decrypt(binascii.unhexlify(encrypted_data)), 16,
                     'pkcs7')

    def get_stream(self, slug, js_data):
        token_data = {}
        token_data['token'] = self.cache.get('token')
        token_data['token_type'] = self.cache.get('token_type')

        if token_data['token'] and token_data['token_type']:
            log.debug('Using cached token')
        else:
            log.debug('Getting new token')

            res = self.session.http.post(
                self.login_url,
                json=js_data['credentials'],
            )
            token_data = self.session.http.json(res, schema=self.token_schema)
            log.debug(f'Token={token_data["token"]}')

            self.cache.set('token',
                           token_data['token'],
                           expires=token_data['expires_in'])
            self.cache.set('token_type',
                           token_data['token_type'],
                           expires=token_data['expires_in'])

        headers = {
            'Authorization':
            f'{token_data["token_type"]} {token_data["token"]}'
        }
        data = {
            'slug': slug,
            'type': js_data['type'],
        }
        res = self.session.http.post(
            self.stream_url,
            headers=headers,
            json=data,
        )
        encrypted_data = self.session.http.json(
            res, schema=self.encrypted_data_schema)

        stream_data = parse_json(
            self.decrypt_data(js_data['cipher_data'], encrypted_data),
            schema=self.stream_schema,
        )

        self.author = stream_data['channel_name']
        self.category = stream_data['genres']
        self.title = stream_data['meta_title']

        return stream_data['live_stream_url']

    def _get_streams(self):
        slug = self.match.group(1)
        log.debug(f'Slug={slug}')

        js_data = self.get_data()
        if not js_data:
            return

        log.debug(f'JS data={js_data}')

        hls_url = self.get_stream(slug, js_data)
        log.debug(f'HLS URL={hls_url}')

        return HLSStream.parse_variant_playlist(self.session, hls_url)
Exemplo n.º 29
0
STREAM_INFO_URL = "https://api.periscope.tv/api/v2/getAccessPublic"

STATUS_GONE = 410
STATUS_UNAVAILABLE = (STATUS_GONE,)

_url_re = re.compile(r"http(s)?://(www\.)?(periscope|pscp)\.tv/[^/]+/(?P<broadcast_id>[\w\-\=]+)")
_stream_schema = validate.Schema(
    validate.any(
        None,
        validate.union({
            "hls_url": validate.all(
                {"hls_url": validate.url(scheme="http")},
                validate.get("hls_url")
            ),
        }),
        validate.union({
            "replay_url": validate.all(
                {"replay_url": validate.url(scheme="http")},
                validate.get("replay_url")
            ),
        }),
    ),
)


class Periscope(Plugin):
    @classmethod
    def can_handle_url(cls, url):
        return _url_re.match(url)
Exemplo n.º 30
0
class PlayTV(Plugin):
    FORMATS_URL = 'http://playtv.fr/player/initialize/{0}/'
    API_URL = 'http://playtv.fr/player/play/{0}/?format={1}&language={2}&bitrate={3}'

    _url_re = re.compile(r'http://playtv\.fr/television/(?P<channel>[^/]+)/?')

    _formats_schema = validate.Schema({
        'streams': validate.any(
            [],
            {
                validate.text: validate.Schema({
                    validate.text: {
                        'bitrates': validate.all([
                            validate.Schema({
                                'value': int
                            })
                        ])
                    }
                })
            }
        )
    })
    _api_schema = validate.Schema({
        'url': validate.url()
    })

    @classmethod
    def can_handle_url(cls, url):
        return PlayTV._url_re.match(url)

    def _get_streams(self):
        match = self._url_re.match(self.url)
        channel = match.group('channel')

        res = http.get(self.FORMATS_URL.format(channel))
        streams = http.json(res, schema=self._formats_schema)['streams']
        if streams == []:
            self.logger.error('Channel may be geo-restricted, not directly provided by PlayTV or not freely available')
            return

        for language in streams:
            for protocol, bitrates in list(streams[language].items()):
                # - Ignore non-supported protocols (RTSP, DASH)
                # - Ignore deprecated Flash (RTMPE) streams (PlayTV doesn't provide anymore a Flash player)
                if protocol in ['rtsp', 'flash', 'dash']:
                    continue

                for bitrate in bitrates['bitrates']:
                    if bitrate['value'] == 0:
                        continue
                    api_url = self.API_URL.format(channel, protocol, language, bitrate['value'])
                    res = http.get(api_url)
                    video_url = http.json(res, schema=self._api_schema)['url']
                    bs = '{0}k'.format(bitrate['value'])

                    if protocol == 'hls':
                        for _, stream in HLSStream.parse_variant_playlist(self.session, video_url).items():
                            yield bs, stream
                    elif protocol == 'hds':
                        for _, stream in HDSStream.parse_manifest(self.session, video_url).items():
                            yield bs, stream
Exemplo n.º 31
0
import re

from streamlink.plugin import Plugin
from streamlink.plugin.api import http, validate
from streamlink.plugin.api import useragents
from streamlink.stream import HLSStream

_url_re = re.compile(r"https?://(www\.)?ok\.ru/live/\d+")
_vod_re = re.compile(r";(?P<hlsurl>[^;]+video\.m3u8.+?)\\&quot;")

_schema = validate.Schema(
    validate.transform(_vod_re.search),
    validate.any(
        None,
        validate.all(
            validate.get("hlsurl"),
            validate.url()
        )
    )
)

class OK_live(Plugin):
    """
    Support for ok.ru live stream: http://www.ok.ru/live/
    """
    @classmethod
    def can_handle_url(cls, url):
        return _url_re.match(url) is not None

    def _get_streams(self):
        headers = {
Exemplo n.º 32
0
class Kugou(Plugin):
    _roomid_re = re.compile(r"roomId:\s*'(\d+)'")
    _room_stream_list_schema = validate.Schema(
        {"data": validate.any(None, {"httpflv": validate.url()})},
        validate.get("httpflv_room_stream_list_schema"))

    _stream_hv_schema = validate.Schema(
        validate.any(
            None,
            [{
                "httpshls": [validate.url()],
                "httpsflv": [validate.url()],
            }],
        ))
    _stream_data_schema = validate.Schema({
        "msg": validate.text,
        "code": int,
        "data": {
            "status": int,
            "vertical": _stream_hv_schema,
            "horizontal": _stream_hv_schema,
            "roomId": int,
        }
    })

    def _get_streams(self):
        res = self.session.http.get(self.url)
        m = self._roomid_re.search(res.text)
        if m:
            room_id = m.group(1)
        else:
            room_id = self.match.group("room_id")

        res = self.session.http.get(
            "https://fx2.service.kugou.com/video/pc/live/pull/v3/streamaddr",
            params={
                "ch": "fx",
                "version": "1.0",
                # 1=rtmp, 2=httpflv, 3=hls, 5=httpsflv, 6=httpshls
                "streamType": "1-2-5-6",
                "ua": "fx-flash",
                "kugouId": "0",
                "roomId": room_id,
                "_": int(time.time()),
            })
        stream_data_json = self.session.http.json(
            res, schema=self._stream_data_schema)
        log.trace("{0!r}".format(stream_data_json))
        if stream_data_json["code"] != 0 or stream_data_json["data"][
                "status"] != 1:
            return

        h = stream_data_json["data"]["horizontal"]
        v = stream_data_json["data"]["vertical"]
        stream_data = h[0] if h else v[0]

        if stream_data.get("httpshls"):
            for hls_url in stream_data["httpshls"]:
                s = HLSStream.parse_variant_playlist(self.session, hls_url)
                if not s:
                    yield "live", HLSStream(self.session, hls_url)
                else:
                    for _s in s.items():
                        yield _s

        if stream_data.get("httpsflv"):
            for http_url in stream_data["httpsflv"]:
                yield "live", HTTPStream(self.session, http_url)
Exemplo n.º 33
0
from streamlink.plugin.api import http, validate
from streamlink.stream import HLSStream, HTTPStream

_url_re = re.compile(r"http(s)?://(\w+\.)?seemeplay.ru/")
_player_re = re.compile(r"""
    SMP.(channel|video).player.init\({
    \s+file:\s+"([^"]+)"
""", re.VERBOSE)

_schema = validate.Schema(
    validate.transform(_player_re.search),
    validate.any(
        None,
        validate.union({
            "type": validate.get(1),
            "url": validate.all(
                validate.get(2),
                validate.url(scheme="http"),
            ),
        })
    )
)


class SeeMePlay(Plugin):
    @classmethod
    def can_handle_url(cls, url):
        return _url_re.match(url)

    def _get_streams(self):
        res = http.get(self.url, schema=_schema)
        if not res:
Exemplo n.º 34
0
class TestDict:
    def test_simple(self):
        schema = {"foo": "FOO", "bar": str}
        value = {"foo": "FOO", "bar": "BAR", "baz": "BAZ"}
        result = validate.validate(schema, value)
        assert result == {"foo": "FOO", "bar": "BAR"}
        assert result is not value

    @pytest.mark.parametrize("value, expected", [
        ({
            "foo": "foo"
        }, {
            "foo": "foo"
        }),
        ({
            "bar": "bar"
        }, {}),
    ],
                             ids=[
                                 "existing",
                                 "missing",
                             ])
    def test_optional(self, value, expected):
        assert validate.validate({validate.optional("foo"): "foo"},
                                 value) == expected

    @pytest.mark.parametrize(
        "schema, value, expected",
        [
            (
                {
                    str: {
                        int: str
                    }
                },
                {
                    "foo": {
                        1: "foo"
                    }
                },
                {
                    "foo": {
                        1: "foo"
                    }
                },
            ),
            (
                {
                    validate.all(str, "foo"): str
                },
                {
                    "foo": "foo"
                },
                {
                    "foo": "foo"
                },
            ),
            (
                {
                    validate.any(int, str): str
                },
                {
                    "foo": "foo"
                },
                {
                    "foo": "foo"
                },
            ),
            (
                {
                    validate.transform(lambda s: s.upper()): str
                },
                {
                    "foo": "foo"
                },
                {
                    "FOO": "foo"
                },
            ),
            (
                {
                    validate.union((str, )): str
                },
                {
                    "foo": "foo"
                },
                {
                    ("foo", ): "foo"
                },
            ),
        ],
        ids=[
            "type",
            "AllSchema",
            "AnySchema",
            "TransformSchema",
            "UnionSchema",
        ],
    )
    def test_keys(self, schema, value, expected):
        assert validate.validate(schema, value) == expected

    def test_failure_key(self):
        with pytest.raises(validate.ValidationError) as cm:
            validate.validate({str: int}, {"foo": 1, 2: 3})
        assert_validationerror(
            cm.value, """
            ValidationError(dict):
              Unable to validate key
              Context(type):
                Type of 2 should be str, but is int
        """)

    def test_failure_key_value(self):
        with pytest.raises(validate.ValidationError) as cm:
            validate.validate({str: int}, {"foo": "bar"})
        assert_validationerror(
            cm.value, """
            ValidationError(dict):
              Unable to validate value
              Context(type):
                Type of 'bar' should be int, but is str
        """)

    def test_failure_notfound(self):
        with pytest.raises(validate.ValidationError) as cm:
            validate.validate({"foo": "bar"}, {"baz": "qux"})
        assert_validationerror(
            cm.value, """
            ValidationError(dict):
              Key 'foo' not found in {'baz': 'qux'}
        """)

    def test_failure_value(self):
        with pytest.raises(validate.ValidationError) as cm:
            validate.validate({"foo": "bar"}, {"foo": 1})
        assert_validationerror(
            cm.value, """
            ValidationError(dict):
              Unable to validate value of key 'foo'
              Context(equality):
                1 does not equal 'bar'
        """)

    def test_failure_schema(self):
        with pytest.raises(validate.ValidationError) as cm:
            validate.validate({}, 1)
        assert_validationerror(
            cm.value, """
            ValidationError(type):
              Type of 1 should be dict, but is int
        """)
Exemplo n.º 35
0
from streamlink.stream import HLSStream
from streamlink.plugin.api import useragents
from streamlink.utils import update_scheme

HUYA_URL = "http://m.huya.com/%s"

_url_re = re.compile(r'http(s)?://(www\.)?huya.com/(?P<channel>[^/]+)', re.VERBOSE)
_hls_re = re.compile(r'^\s*<video\s+id="html5player-video"\s+src="(?P<url>[^"]+)"', re.MULTILINE)

_hls_schema = validate.Schema(
    validate.all(
        validate.transform(_hls_re.search),
        validate.any(
            None,
            validate.all(
                validate.get('url'),
                validate.transform(str)
            )
        )
    )
)


class Huya(Plugin):
    @classmethod
    def can_handle_url(self, url):
        return _url_re.match(url)

    def _get_streams(self):
        match = _url_re.match(self.url)
        channel = match.group("channel")
Exemplo n.º 36
0
 def schema(self):
     return validate.any(
         "foo",
         str,
         lambda data: data is not None,
     )
Exemplo n.º 37
0
    "devicetype=desktop&"
    "preferred-player-odm=hlslink&"
    "preferred-player-live=hlslink"
)

_id_re = re.compile(r"/(?:program|direkte|serie/[^/]+)/([^/]+)")
_url_re = re.compile(r"https?://(tv|radio).nrk.no/")
_api_baseurl_re = re.compile(r'''apiBaseUrl:\s*["'](?P<baseurl>[^"']+)["']''')

_schema = validate.Schema(
    validate.transform(_api_baseurl_re.search),
    validate.any(
        None,
        validate.all(
            validate.get("baseurl"),
            validate.url(
                scheme="http"
            )
        )
    )
)

_mediaelement_schema = validate.Schema({
    "mediaUrl": validate.url(
        scheme="http",
        path=validate.endswith(".m3u8")
    )
})


class NRK(Plugin):
class AnimeLab(Plugin):
    url_re = re.compile(r"https?://(?:www\.)?animelab\.com/player/")
    login_url = "https://www.animelab.com/login"
    video_collection_re = re.compile(r"VideoCollection\((\[.*?\])\);")
    playlist_position_re = re.compile(r"playlistPosition\s*=\s*(\d+);")
    video_collection_schema = validate.Schema(
        validate.union({
            "position":
            validate.all(
                validate.transform(playlist_position_re.search),
                validate.any(
                    None, validate.all(validate.get(1),
                                       validate.transform(int)))),
            "playlist":
            validate.all(
                validate.transform(video_collection_re.search),
                validate.any(
                    None,
                    validate.all(validate.get(1),
                                 validate.transform(parse_json))))
        }))
    arguments = PluginArguments(
        PluginArgument(
            "email",
            requires=["password"],
            metavar="EMAIL",
            help="The email address used to register with animelab.com."),
        PluginArgument(
            "password",
            sensitive=True,
            metavar="PASSWORD",
            help="A animelab.com account password to use with --animelab-email."
        ))

    @classmethod
    def can_handle_url(cls, url):
        return cls.url_re.match(url) is not None

    def login(self, email, password):
        self.logger.debug("Attempting to log in as {0}", email)
        res = self.session.http.post(self.login_url,
                                     data=dict(email=email, password=password),
                                     allow_redirects=False,
                                     raise_for_status=False)
        loc = res.headers.get("Location", "")
        if "geoblocked" in loc.lower():
            self.logger.error("AnimeLab is not available in your territory")
        elif res.status_code >= 400:
            self.logger.error(
                "Failed to login to AnimeLab, check your email/password combination"
            )
        else:
            return True

        return False

    def _get_streams(self):
        email, password = self.get_option("email"), self.get_option("password")
        if not email or not password:
            self.logger.error(
                "AnimeLab requires authentication, use --animelab-email "
                "and --animelab-password to set your email/password combination"
            )
            return

        if self.login(email, password):
            self.logger.info("Successfully logged in as {0}", email)
            video_collection = self.session.http.get(
                self.url, schema=self.video_collection_schema)
            if video_collection["playlist"] is None or video_collection[
                    "position"] is None:
                return

            data = video_collection["playlist"][video_collection["position"]]

            self.logger.debug("Found {0} version {1} hard-subs",
                              data["language"]["name"],
                              "with" if data["hardSubbed"] else "without")

            for video in data["videoInstances"]:
                if video["httpUrl"]:
                    q = video["videoQuality"]["description"]
                    s = HTTPStream(self.session, video["httpUrl"])
                    yield q, s
Exemplo n.º 39
0
        }
    },
    validate.get("chansub")
)
_user_schema = validate.Schema(
    {
        validate.optional("display_name"): validate.text
    },
    validate.get("display_name")
)
_video_schema = validate.Schema(
    {
        "chunks": {
            validate.text: [{
                "length": int,
                "url": validate.any(None, validate.url(scheme="http")),
                "upkeep": validate.any("pass", "fail", None)
            }]
        },
        "restrictions": {validate.text: validate.text},
        "start_offset": int,
        "end_offset": int,
    }
)
_viewer_info_schema = validate.Schema(
    {
        validate.optional("login"): validate.text
    },
    validate.get("login")
)
_viewer_token_schema = validate.Schema(
Exemplo n.º 40
0
STREAM_INFO_URL = "https://api.periscope.tv/api/v2/getAccessPublic"

STATUS_GONE = 410
STATUS_UNAVAILABLE = (STATUS_GONE, )

_url_re = re.compile(
    r"http(s)?://(www\.)?periscope.tv/[^/]+/(?P<broadcast_id>[\w\-\=]+)")
_stream_schema = validate.Schema(
    validate.any(
        None,
        validate.union({
            "hls_url":
            validate.all({"hls_url": validate.url(scheme="http")},
                         validate.get("hls_url")),
        }),
        validate.union({
            "replay_url":
            validate.all({"replay_url": validate.url(scheme="http")},
                         validate.get("replay_url")),
        }),
    ), )


class Periscope(Plugin):
    @classmethod
    def can_handle_url(cls, url):
        return _url_re.match(url)

    def _get_streams(self):
        match = _url_re.match(self.url)
Exemplo n.º 41
0
import re

from streamlink.plugin import Plugin
from streamlink.plugin.api import http, validate

YOUTUBE_URL = "https://www.youtube.com/watch?v={0}"
_url_re = re.compile(r'http(s)?://www\.skai.gr/.*')
_youtube_id = re.compile(r'<span\s+itemprop="contentUrl"\s+href="(.*)"></span>', re.MULTILINE)
_youtube_url_schema = validate.Schema(
    validate.all(
        validate.transform(_youtube_id.search),
        validate.any(
            None,
            validate.all(
                validate.get(1),
                validate.text
            )
        )
    )
)


class Skai(Plugin):
    @classmethod
    def can_handle_url(cls, url):
        return _url_re.match(url)

    def _get_streams(self):
        channel_id = http.get(self.url, schema=_youtube_url_schema)
        if channel_id:
            return self.session.streams(YOUTUBE_URL.format(channel_id))
Exemplo n.º 42
0
    "h264_aac_ts_http_m3u8_http": ("HLS", HLSStream.parse_variant_playlist)
}

_url_re = re.compile(r"""
    http(s)?://(\w+\.)?zdf.de/
""", re.VERBOSE | re.IGNORECASE)
_api_json_re = re.compile(r'''data-zdfplayer-jsb=["'](?P<json>{.+?})["']''',
                          re.S)

_api_schema = validate.Schema(
    validate.transform(_api_json_re.search),
    validate.any(
        None,
        validate.all(
            validate.get("json"),
            validate.transform(parse_json),
            {
                "content": validate.text,
                "apiToken": validate.text
            },
        )))

_documents_schema = validate.Schema({
    "mainVideoContent": {
        "http://zdf.de/rels/target": {
            "http://zdf.de/rels/streams/ptmd-template": validate.text
        },
    },
})

_schema = validate.Schema({
    "priorityList": [{
Exemplo n.º 43
0
from streamlink.plugin import Plugin
from streamlink.plugin.api import StreamMapper, http, validate
from streamlink.stream import HDSStream, HLSStream, RTMPStream
from streamlink.utils import rtmpparse

STREAM_API_URL = "https://playapi.mtgx.tv/v3/videos/stream/{0}"

_swf_url_re = re.compile(r"data-flashplayer-url=\"([^\"]+)\"")
_player_data_re = re.compile(r"window.fluxData\s*=\s*JSON.parse\(\"(.+)\"\);")

_stream_schema = validate.Schema(
    validate.any(
        None,
        validate.all({"msg": validate.text}),
        validate.all({
            "streams": validate.all(
                {validate.text: validate.any(validate.text, int, None)},
                validate.filter(lambda k, v: isinstance(v, validate.text))
            )
        }, validate.get("streams"))
    )
)


class Viasat(Plugin):
    """Streamlink Plugin for Viasat"""

    _iframe_re = re.compile(r"""<iframe.+src=["'](?P<url>[^"']+)["'].+allowfullscreen""")
    _image_re = re.compile(r"""<meta\sproperty=["']og:image["']\scontent=".+/(?P<stream_id>\d+)/[^/]+\.jpg""")

    _url_re = re.compile(r"""https?://(?:www\.)?
        (?:
Exemplo n.º 44
0
class SteamBroadcastPlugin(Plugin):
    _url_re = re.compile(r"https?://steamcommunity.com/broadcast/watch/(\d+)")
    _steamtv_url_re = re.compile(r"https?://steam.tv/(\w+)")
    _watch_broadcast_url = "https://steamcommunity.com/broadcast/watch/"
    _get_broadcast_url = "https://steamcommunity.com/broadcast/getbroadcastmpd/"
    _user_agent = "streamlink/{}".format(streamlink.__version__)
    _broadcast_schema = Schema({
        "success": validate.any("ready", "unavailable", "waiting", "waiting_to_start", "waiting_for_start"),
        "retry": int,
        "broadcastid": validate.any(validate.text, int),
        validate.optional("url"): validate.url(),
        validate.optional("viewertoken"): validate.text
    })
    _get_rsa_key_url = "https://steamcommunity.com/login/getrsakey/"
    _rsa_key_schema = validate.Schema({
        "publickey_exp": validate.all(validate.text, validate.transform(lambda x: int(x, 16))),
        "publickey_mod": validate.all(validate.text, validate.transform(lambda x: int(x, 16))),
        "success": True,
        "timestamp": validate.text,
        "token_gid": validate.text
    })
    _dologin_url = "https://steamcommunity.com/login/dologin/"
    _dologin_schema = validate.Schema({
        "success": bool,
        "requires_twofactor": bool,
        validate.optional("message"): validate.text,
        validate.optional("emailauth_needed"): bool,
        validate.optional("emaildomain"): validate.text,
        validate.optional("emailsteamid"): validate.text,
        validate.optional("login_complete"): bool,
        validate.optional("captcha_needed"): bool,
        validate.optional("captcha_gid"): validate.any(validate.text, int)
    })
    _captcha_url = "https://steamcommunity.com/public/captcha.php?gid={}"

    arguments = PluginArguments(
        PluginArgument(
            "email",
            metavar="EMAIL",
            requires=["password"],
            help="""
            A Steam account email address to access friends/private streams
            """
        ),
        PluginArgument(
            "password",
            metavar="PASSWORD",
            sensitive=True,
            help="""
            A Steam account password to use with --steam-email.
            """
        ))

    def __init__(self, url):
        super(SteamBroadcastPlugin, self).__init__(url)
        self.session.http.headers["User-Agent"] = self._user_agent

    @classmethod
    def can_handle_url(cls, url):
        return cls._url_re.match(url) is not None or cls._steamtv_url_re.match(url) is not None

    @property
    def donotcache(self):
        return str(int(time.time() * 1000))

    def encrypt_password(self, email, password):
        """
        Get the RSA key for the user and encrypt the users password
        :param email: steam account
        :param password: password for account
        :return: encrypted password
        """
        res = self.session.http.get(self._get_rsa_key_url, params=dict(username=email, donotcache=self.donotcache))
        rsadata = self.session.http.json(res, schema=self._rsa_key_schema)

        rsa = RSA.construct((rsadata["publickey_mod"], rsadata["publickey_exp"]))
        cipher = PKCS1_v1_5.new(rsa)
        return base64.b64encode(cipher.encrypt(password.encode("utf8"))), rsadata["timestamp"]

    def dologin(self, email, password, emailauth="", emailsteamid="", captchagid="-1", captcha_text="", twofactorcode=""):
        """
        Logs in to Steam

        """
        epassword, rsatimestamp = self.encrypt_password(email, password)

        login_data = {
            'username': email,
            "password": epassword,
            "emailauth": emailauth,
            "loginfriendlyname": "Streamlink",
            "captchagid": captchagid,
            "captcha_text": captcha_text,
            "emailsteamid": emailsteamid,
            "rsatimestamp": rsatimestamp,
            "remember_login": True,
            "donotcache": self.donotcache,
            "twofactorcode": twofactorcode
        }

        res = self.session.http.post(self._dologin_url, data=login_data)

        resp = self.session.http.json(res, schema=self._dologin_schema)

        if not resp[u"success"]:
            if resp.get(u"captcha_needed"):
                # special case for captcha
                captchagid = resp[u"captcha_gid"]
                log.error("Captcha result required, open this URL to see the captcha: {}".format(
                    self._captcha_url.format(captchagid)))
                try:
                    captcha_text = self.input_ask("Captcha text")
                except FatalPluginError:
                    captcha_text = None
                if not captcha_text:
                    return False
            else:
                # If the user must enter the code that was emailed to them
                if resp.get(u"emailauth_needed"):
                    if not emailauth:
                        try:
                            emailauth = self.input_ask("Email auth code required")
                        except FatalPluginError:
                            emailauth = None
                        if not emailauth:
                            return False
                    else:
                        raise SteamLoginFailed("Email auth key error")

                # If the user must enter a two factor auth code
                if resp.get(u"requires_twofactor"):
                    try:
                        twofactorcode = self.input_ask("Two factor auth code required")
                    except FatalPluginError:
                        twofactorcode = None
                    if not twofactorcode:
                        return False

                if resp.get(u"message"):
                    raise SteamLoginFailed(resp[u"message"])

            return self.dologin(email, password,
                                emailauth=emailauth,
                                emailsteamid=resp.get(u"emailsteamid", u""),
                                captcha_text=captcha_text,
                                captchagid=captchagid,
                                twofactorcode=twofactorcode)
        elif resp.get("login_complete"):
            return True
        else:
            log.error("Something when wrong when logging in to Steam")
            return False

    def login(self, email, password):
        log.info("Attempting to login to Steam as {}".format(email))
        return self.dologin(email, password)

    def _get_broadcast_stream(self, steamid, viewertoken=0, sessionid=None):
        log.debug("Getting broadcast stream: sessionid={0}".format(sessionid))
        res = self.session.http.get(self._get_broadcast_url,
                                    params=dict(broadcastid=0,
                                                steamid=steamid,
                                                viewertoken=viewertoken,
                                                sessionid=sessionid))
        return self.session.http.json(res, schema=self._broadcast_schema)

    def _get_streams(self):
        streamdata = None
        if self.get_option("email"):
            if self.login(self.get_option("email"), self.get_option("password")):
                log.info("Logged in as {0}".format(self.get_option("email")))
                self.save_cookies(lambda c: "steamMachineAuth" in c.name)

        # Handle steam.tv URLs
        if self._steamtv_url_re.match(self.url) is not None:
            # extract the steam ID from the page
            res = self.session.http.get(self.url)
            for div in itertags(res.text, 'div'):
                if div.attributes.get("id") == "webui_config":
                    broadcast_data = html_unescape(div.attributes.get("data-broadcast"))
                    steamid = parse_json(broadcast_data).get("steamid")
                    self.url = self._watch_broadcast_url + steamid

        # extract the steam ID from the URL
        steamid = self._url_re.match(self.url).group(1)
        res = self.session.http.get(self.url)  # get the page to set some cookies
        sessionid = res.cookies.get('sessionid')

        while streamdata is None or streamdata[u"success"] in ("waiting", "waiting_for_start"):
            streamdata = self._get_broadcast_stream(steamid,
                                                    sessionid=sessionid)

            if streamdata[u"success"] == "ready":
                return DASHStream.parse_manifest(self.session, streamdata["url"])
            elif streamdata[u"success"] == "unavailable":
                log.error("This stream is currently unavailable")
                return
            else:
                r = streamdata[u"retry"] / 1000.0
                log.info("Waiting for stream, will retry again in {} seconds...".format(r))
                time.sleep(r)
Exemplo n.º 45
0
_api_schema = validate.Schema({
    "error": bool,
    validate.optional("code"): validate.text,
    validate.optional("message"): validate.text,
    validate.optional("data"): object,
})
_media_schema = validate.Schema(
    {
        "stream_data": validate.any(
            None,
            {
                "streams": validate.all(
                    [{
                        "quality": validate.any(validate.text, None),
                        "url": validate.url(
                            scheme="http",
                            path=validate.endswith(".m3u8")
                        ),
                        validate.optional("video_encode_id"): validate.text
                    }]
                )
            }
        )
    },
    validate.get("stream_data")
)
_login_schema = validate.Schema({
    "auth": validate.text,
    "expires": validate.all(
        validate.text,
        validate.transform(parse_timestamp)
Exemplo n.º 46
0
class Filmon(Plugin):
    url_re = re.compile(r"""(?x)https?://(?:www\.)?filmon\.(?:tv|com)/(?:
        (?:
            index/popout\?
            |
            (?:tv/)channel/(?:export\?)?
            |
            tv/(?!channel/)
            |
            channel/
            |
            (?P<is_group>group/)
        )(?:channel_id=)?(?P<channel>[-_\w]+)
    |
        vod/view/(?P<vod_id>\d+)-
    )""")

    _channel_id_re = re.compile(
        r"""channel_id\s*=\s*(?P<quote>['"]?)(?P<value>\d+)(?P=quote)""")
    _channel_id_schema = validate.Schema(
        validate.transform(_channel_id_re.search),
        validate.any(None, validate.get("value")))

    quality_weights = {"high": 720, "low": 480}

    TIME_CHANNEL = 60 * 60 * 24 * 365

    def __init__(self, url):
        super(Filmon, self).__init__(url)
        parsed = urlparse(self.url)
        if parsed.path.startswith("/channel/"):
            self.url = urlunparse(
                parsed._replace(path=parsed.path.replace("/channel/", "/tv/")))
        self.api = FilmOnAPI(self.session)

    @classmethod
    def can_handle_url(cls, url):
        return cls.url_re.match(url) is not None

    @classmethod
    def stream_weight(cls, key):
        weight = cls.quality_weights.get(key)
        if weight:
            return weight, "filmon"

        return Plugin.stream_weight(key)

    def _get_streams(self):
        url_m = self.url_re.match(self.url)

        channel = url_m and url_m.group("channel")
        vod_id = url_m and url_m.group("vod_id")
        is_group = url_m and url_m.group("is_group")

        if vod_id:
            data = self.api.vod(vod_id)
            for _, stream in data["streams"].items():
                streams = HLSStream.parse_variant_playlist(
                    self.session, stream["url"])
                if not streams:
                    yield stream["quality"], HLSStream(self.session,
                                                       stream["url"])
                else:
                    yield from streams.items()
        else:
            if channel and not channel.isdigit():
                _id = self.cache.get(channel)
                if _id is None:
                    _id = self.session.http.get(self.url,
                                                schema=self._channel_id_schema)
                    log.debug(f"Found channel ID: {_id}")
                    # do not cache a group url
                    if _id and not is_group:
                        self.cache.set(channel, _id, expires=self.TIME_CHANNEL)
                else:
                    log.debug(f"Found cached channel ID: {_id}")
            else:
                _id = channel

            if _id is None:
                raise PluginError(
                    "Unable to find channel ID: {0}".format(channel))

            try:
                data = self.api.channel(_id)
                for stream in data["streams"]:
                    yield stream["quality"], FilmOnHLS(
                        self.session, channel=_id, quality=stream["quality"])
            except Exception:
                if channel and not channel.isdigit():
                    self.cache.set(channel, None, expires=0)
                    log.debug(f"Reset cached channel: {channel}")

                raise
Exemplo n.º 47
0
    "web_medium": 10,
    "web_high": 11,
    "web_hd": 12
}

_url_re = re.compile(r"http(s)?://(\w+\.)?be-at.tv/")
_schema = validate.Schema(
    validate.any(
        None,
        {
            "status": int,
            "media": [{
                "duration": validate.any(float, int),
                "offset": validate.any(float, int),
                "id": int,
                "parts": [{
                    "duration": validate.any(float, int),
                    "id": int,
                    "offset": validate.any(float, int),
                    validate.optional("recording"): int,
                    validate.optional("start"): validate.any(float, int)
                }]
            }]
        }
    )
)

Chunk = namedtuple("Chunk", "recording quality sequence extension")


class BeatFLVTagConcat(FLVTagConcat):
    def __init__(self, *args, **kwargs):
Exemplo n.º 48
0
class UStreamTVWsClient(WebsocketClient):
    API_URL = "wss://r{0}-1-{1}-{2}-ws-{3}.ums.services.video.ibm.com/1/ustream"
    APP_ID = 3
    APP_VERSION = 2

    STREAM_OPENED_TIMEOUT = 6

    _schema_cmd = validate.Schema({
        "cmd": str,
        "args": [{
            str: object
        }],
    })
    _schema_stream_formats = validate.Schema({
        "streams": [
            validate.any(
                validate.all(
                    {
                        "contentType": "video/mp4",
                        "sourceStreamVersion": int,
                        "initUrl": str,
                        "segmentUrl": str,
                        "bitrate": int,
                        "height": int,
                    },
                    validate.transform(lambda obj: StreamFormatVideo(**obj))),
                validate.all(
                    {
                        "contentType": "audio/mp4",
                        "sourceStreamVersion": int,
                        "initUrl": str,
                        "segmentUrl": str,
                        "bitrate": int,
                        validate.optional("language"): str,
                    },
                    validate.transform(lambda obj: StreamFormatAudio(**obj))),
                object)
        ]
    })
    _schema_stream_segments = validate.Schema({
        "chunkId":
        int,
        "chunkTime":
        int,
        "contentAccess":
        validate.all({"accessList": [{
            "data": {
                "path": str
            }
        }]}, validate.get(("accessList", 0, "data", "path"))),
        "hashes": {
            validate.transform(int): str
        }
    })

    stream_cdn: str = None
    stream_formats_video: List[StreamFormatVideo] = None
    stream_formats_audio: List[StreamFormatAudio] = None
    stream_initial_id: int = None

    def __init__(self,
                 session,
                 media_id,
                 application,
                 referrer=None,
                 cluster="live",
                 password=None,
                 app_id=APP_ID,
                 app_version=APP_VERSION):
        self.opened = Event()
        self.ready = Event()
        self.stream_error = None
        # a list of deques subscribed by worker threads which independently need to read segments
        self.stream_segments_subscribers: List[Deque[Segment]] = []
        self.stream_segments_initial: Deque[Segment] = deque()
        self.stream_segments_lock = RLock()

        self.media_id = media_id
        self.application = application
        self.referrer = referrer
        self.cluster = cluster
        self.password = password
        self.app_id = app_id
        self.app_version = app_version

        super().__init__(session,
                         self._get_url(),
                         origin="https://www.ustream.tv")

    def _get_url(self):
        return self.API_URL.format(randint(0, 0xffffff), self.media_id,
                                   self.application, self.cluster)

    def _set_error(self, error: Any):
        self.stream_error = error
        self.ready.set()

    def _set_ready(self):
        if not self.ready.is_set(
        ) and self.stream_cdn and self.stream_initial_id is not None:
            self.ready.set()

            if self.opened.wait(self.STREAM_OPENED_TIMEOUT):
                log.debug("Stream opened, keeping websocket connection alive")
            else:
                log.info("Closing websocket connection")
                self.ws.close()

    def segments_subscribe(self) -> Deque[Segment]:
        with self.stream_segments_lock:
            # copy the initial segments deque (segments arrive early)
            new_deque = self.stream_segments_initial.copy()
            self.stream_segments_subscribers.append(new_deque)

            return new_deque

    def _segments_append(self, segment: Segment):
        # if there are no subscribers yet, add segment(s) to the initial deque
        if not self.stream_segments_subscribers:
            self.stream_segments_initial.append(segment)
        else:
            for subscriber_deque in self.stream_segments_subscribers:
                subscriber_deque.append(segment)

    def on_open(self, wsapp):
        args = {
            "type": "viewer",
            "appId": self.app_id,
            "appVersion": self.app_version,
            "rsid":
            f"{randint(0, 10_000_000_000):x}:{randint(0, 10_000_000_000):x}",
            "rpin": f"_rpin.{randint(0, 1_000_000_000_000_000)}",
            "referrer": self.referrer,
            "clusterHost":
            "r%rnd%-1-%mediaId%-%mediaType%-%protocolPrefix%-%cluster%.ums.ustream.tv",
            "media": self.media_id,
            "application": self.application
        }
        if self.password:
            args["password"] = self.password

        self.send_json({"cmd": "connect", "args": [args]})

    def on_message(self, wsapp, data: str):
        try:
            parsed = parse_json(data, schema=self._schema_cmd)
        except PluginError:
            log.error(f"Could not parse message: {data[:50]}")
            return

        cmd: str = parsed["cmd"]
        args: List[Dict] = parsed["args"]
        log.trace(f"Received '{cmd}' command")
        log.trace(f"{args!r}")

        handlers = self._MESSAGE_HANDLERS.get(cmd)
        if handlers is not None:
            for arg in args:
                for name, handler in handlers.items():
                    argdata = arg.get(name)
                    if argdata is not None:
                        log.debug(f"Processing '{cmd}' - '{name}'")
                        handler(self, argdata)

    # noinspection PyMethodMayBeStatic
    def _handle_warning(self, data: Dict):
        log.warning(f"{data['code']}: {str(data['message'])[:50]}")

    # noinspection PyUnusedLocal
    def _handle_reject_nonexistent(self, *args):
        self._set_error("This channel does not exist")

    # noinspection PyUnusedLocal
    def _handle_reject_geo_lock(self, *args):
        self._set_error("This content is not available in your area")

    def _handle_reject_cluster(self, arg: Dict):
        self.cluster = arg["name"]
        log.info(f"Switching cluster to: {self.cluster}")
        self.reconnect(url=self._get_url())

    def _handle_reject_referrer_lock(self, arg: Dict):
        self.referrer = arg["redirectUrl"]
        log.info(f"Updating referrer to: {self.referrer}")
        self.reconnect(url=self._get_url())

    def _handle_module_info_cdn_config(self, data: Dict):
        self.stream_cdn = urlunparse(
            (data["protocol"], data["data"][0]["data"][0]["sites"][0]["host"],
             data["data"][0]["data"][0]["sites"][0]["path"], "", "", ""))
        self._set_ready()

    def _handle_module_info_stream(self, data: Dict):
        if data.get("contentAvailable") is False:
            return self._set_error("This stream is currently offline")

        mp4_segmented = data.get("streamFormats", {}).get("mp4/segmented")
        if not mp4_segmented:
            return

        # parse the stream formats once
        if self.stream_initial_id is None:
            try:
                formats = self._schema_stream_formats.validate(mp4_segmented)
                formats = formats["streams"]
            except PluginError as err:
                return self._set_error(err)
            self.stream_formats_video = list(
                filter(lambda f: type(f) is StreamFormatVideo, formats))
            self.stream_formats_audio = list(
                filter(lambda f: type(f) is StreamFormatAudio, formats))

        # parse segment duration and hashes, and queue new segments
        try:
            segmentdata: Dict = self._schema_stream_segments.validate(
                mp4_segmented)
        except PluginError:
            log.error("Failed parsing hashes")
            return

        current_id: int = segmentdata["chunkId"]
        duration: int = segmentdata["chunkTime"]
        path: str = segmentdata["contentAccess"]
        hashes: Dict[int, str] = segmentdata["hashes"]

        sorted_ids = sorted(hashes.keys())
        count = len(sorted_ids)
        if count == 0:
            return

        # initial segment ID (needed by the workers to filter queued segments)
        if self.stream_initial_id is None:
            self.stream_initial_id = current_id

        current_time = datetime.now()

        # lock the stream segments deques for the worker threads
        with self.stream_segments_lock:
            # interpolate and extrapolate segments from the provided id->hash data
            diff = 10 - sorted_ids[
                0] % 10  # if there's only one id->hash item, extrapolate until the next decimal
            for idx, segment_id in enumerate(sorted_ids):
                idx_next = idx + 1
                if idx_next < count:
                    # calculate the difference between IDs and use that to interpolate segment IDs
                    # the last id->hash item will use the previous diff to extrapolate segment IDs
                    diff = sorted_ids[idx_next] - segment_id
                for num in range(segment_id, segment_id + diff):
                    self._segments_append(
                        Segment(num=num,
                                duration=duration,
                                available_at=current_time +
                                timedelta(seconds=(num - current_id - 1) *
                                          duration / 1000),
                                hash=hashes[segment_id],
                                path=path))

        self._set_ready()

    # ----

    _MESSAGE_HANDLERS: Dict[str, Dict[str, Callable[
        ["UStreamTVWsClient", Any], None]]] = {
            "warning": {
                "code": _handle_warning,
            },
            "reject": {
                "cluster": _handle_reject_cluster,
                "referrerLock": _handle_reject_referrer_lock,
                "nonexistent": _handle_reject_nonexistent,
                "geoLock": _handle_reject_geo_lock,
            },
            "moduleInfo": {
                "cdnConfig": _handle_module_info_cdn_config,
                "stream": _handle_module_info_stream,
            }
        }
Exemplo n.º 49
0
API_URL = "https://www.zhanqi.tv/api/static/v2.1/room/domain/{0}.json"

STATUS_ONLINE = 4
STATUS_OFFLINE = 0

_url_re = re.compile(r"""
    http(s)?://(www\.)?zhanqi.tv
    /(?P<channel>[^/]+)
""", re.VERBOSE)

_room_schema = validate.Schema(
    {
        "data": validate.any(None, {
            "status": validate.all(
                validate.text,
                validate.transform(int)
            ),
            "videoId": validate.text
        })
    },
    validate.get("data")
)


class Zhanqitv(Plugin):
    @classmethod
    def can_handle_url(self, url):
        return _url_re.match(url)

    def _get_streams(self):
        match = _url_re.match(self.url)
Exemplo n.º 50
0
import random
import re
import json

from streamlink.plugin import Plugin
from streamlink.plugin.api import http
from streamlink.plugin.api import validate
from streamlink.plugin.api import useragents
from streamlink.stream import HLSStream

_url_re = re.compile(r"http(s)?://(www\.)?camsoda\.com/(?P<username>[^\"\']+)")

_api_user_schema = validate.Schema(
    {
        "status": validate.any(int, validate.text),
        validate.optional("user"): validate.Schema({
            "chat": validate.Schema ({
                    "status": validate.any(int, validate.text)
            })
        })
    }
)

_api_video_schema = validate.Schema(
    {
        "token": validate.text,
        "app": validate.text,
        "edge_servers": [validate.text],
        "stream_name": validate.text
    }
)
Exemplo n.º 51
0
from streamlink.plugin.api import http, validate
from streamlink.stream import FLVPlaylist, HTTPStream

API_URL = "http://veetle.com/index.php/stream/ajaxStreamLocation/{0}/flash"

_url_re = re.compile("""
    http(s)?://(\w+\.)?veetle.com
    (:?
        /.*(v|view)/
        (?P<channel>[^/]+/[^/&?]+)
    )?
""", re.VERBOSE)

_schema = validate.Schema({
    validate.optional("isLive"): bool,
    "payload": validate.any(int, validate.url(scheme="http")),
    "success": bool
})


class Veetle(Plugin):
    @classmethod
    def can_handle_url(self, url):
        return _url_re.match(url)

    def _get_streams(self):
        self.url = http.resolve_url(self.url)
        match = _url_re.match(self.url)
        parsed = urlparse(self.url)
        if parsed.fragment:
            channel_id = parsed.fragment
Exemplo n.º 52
0
class LineLive(Plugin):
    _api_url = "https://live-api.line-apps.com/web/v4.0/channel/{0}/broadcast/{1}/player_status"

    _player_status_schema = validate.Schema(
        {
            "liveStatus": validate.text,
            "liveHLSURLs": validate.any(None, {
                "720": validate.any(None, validate.url(scheme="http", path=validate.endswith(".m3u8"))),
                "480": validate.any(None, validate.url(scheme="http", path=validate.endswith(".m3u8"))),
                "360": validate.any(None, validate.url(scheme="http", path=validate.endswith(".m3u8"))),
                "240": validate.any(None, validate.url(scheme="http", path=validate.endswith(".m3u8"))),
                "144": validate.any(None, validate.url(scheme="http", path=validate.endswith(".m3u8"))),
            }),
            "archivedHLSURLs": validate.any(None, {
                "720": validate.any(None, validate.url(scheme="http", path=validate.endswith(".m3u8"))),
                "480": validate.any(None, validate.url(scheme="http", path=validate.endswith(".m3u8"))),
                "360": validate.any(None, validate.url(scheme="http", path=validate.endswith(".m3u8"))),
                "240": validate.any(None, validate.url(scheme="http", path=validate.endswith(".m3u8"))),
                "144": validate.any(None, validate.url(scheme="http", path=validate.endswith(".m3u8"))),
            }),
        })

    def _get_live_streams(self, json):
        for stream in json["liveHLSURLs"]:
            url = json["liveHLSURLs"][stream]
            if url is not None:
                yield "{0}p.".format(stream), HLSStream(self.session, url)

    def _get_vod_streams(self, json):
        for stream in json["archivedHLSURLs"]:
            url = json["archivedHLSURLs"][stream]
            if url is not None:
                yield "{0}p.".format(stream), HLSStream(self.session, url)

    def _get_streams(self):
        channel = self.match.group("channel")
        broadcast = self.match.group("broadcast")
        res = self.session.http.get(self._api_url.format(channel, broadcast))
        json = self.session.http.json(res, schema=self._player_status_schema)
        if json["liveStatus"] == "LIVE":
            return self._get_live_streams(json)
        elif json["liveStatus"] == "FINISHED":
            return self._get_vod_streams(json)
        return
Exemplo n.º 53
0
_url_re = re.compile("""
    http(s)?://(www\.)?douyu.com
    /(?P<channel>[^/]+)
""", re.VERBOSE)

_room_id_re = re.compile(r'"room_id"\s*:\s*(\d+),')
_room_id_alt_re = re.compile(r'data-room_id="(\d+)"')

_room_id_schema = validate.Schema(
    validate.all(
        validate.transform(_room_id_re.search),
        validate.any(
            None,
            validate.all(
                validate.get(1),
                validate.transform(int)
            )
        )
    )
)

_room_id_alt_schema = validate.Schema(
    validate.all(
        validate.transform(_room_id_alt_re.search),
        validate.any(
            None,
            validate.all(
                validate.get(1),
                validate.transform(int)
            )
Exemplo n.º 54
0
from streamlink.plugin.api import validate
from streamlink.plugin.api.utils import parse_json

__all__ = ["parse_playlist"]

_playlist_re = re.compile("\(?\{.*playlist: (\[.*\]),.*?\}\)?;", re.DOTALL)
_js_to_json = partial(re.compile("(\w+):\s").sub, r'"\1":')

_playlist_schema = validate.Schema(
    validate.transform(_playlist_re.search),
    validate.any(
        None,
        validate.all(
            validate.get(1),
            validate.transform(_js_to_json),
            validate.transform(parse_json),
            [{
                "sources": [{
                    "file": validate.text,
                    validate.optional("label"): validate.text
                }]
            }]
        )
    )
)


def parse_playlist(res):
    """Attempts to parse a JWPlayer playlist in a HTTP response body."""
    return _playlist_schema.validate(res.text)
Exemplo n.º 55
0
import re

from streamlink.plugin import Plugin, PluginError, PluginOptions
from streamlink.plugin.api import http, validate
from streamlink.stream import HLSStream

LOGIN_PAGE_URL = "http://www.livestation.com/en/users/new"
LOGIN_POST_URL = "http://www.livestation.com/en/sessions.json"

_csrf_token_re = re.compile("<meta content=\"([^\"]+)\" name=\"csrf-token\"")
_hls_playlist_re = re.compile("<meta content=\"([^\"]+.m3u8)\" property=\"og:video\" />")
_url_re = re.compile("http(s)?://(\w+\.)?livestation.com")

_csrf_token_schema = validate.Schema(
    validate.transform(_csrf_token_re.search),
    validate.any(None, validate.get(1))
)
_hls_playlist_schema = validate.Schema(
    validate.transform(_hls_playlist_re.search),
    validate.any(
        None,
        validate.all(
            validate.get(1),
            validate.url(scheme="http", path=validate.endswith(".m3u8"))
        )
    )
)
_login_schema = validate.Schema({
    "email": validate.text,
    validate.optional("errors"): validate.all(
        {
Exemplo n.º 56
0
VIEW_LIVE_API_URL_JP = "http://api.afreecatv.jp/live/view_live.php"

_url_re = re.compile(
    "http(s)?://(\w+\.)?(afreecatv.com.tw|afreeca.tv|afreecatv.jp)/(?P<channel>[\w\-_]+)"
)
_url_re_tw = re.compile(
    "http(s)?://(\w+\.)?(afreecatv.com.tw)/(?P<channel>[\w\-_]+)")
_url_re_jp = re.compile(
    "http(s)?://(\w+\.)?(afreecatv.jp)/(?P<channel>[\w\-_]+)")
_flashvars_re = re.compile('<param name="flashvars" value="([^"]+)" />')

_flashvars_schema = validate.Schema(
    validate.transform(_flashvars_re.findall), validate.get(0),
    validate.transform(parse_query),
    validate.any({
        "s": validate.text,
        "id": validate.text
    }, {}))
_view_live_schema = validate.Schema(
    {
        "channel": {
            "strm": [{
                "bps": validate.text,
                "purl": validate.url(scheme="rtmp")
            }]
        },
    }, validate.get("channel"), validate.get("strm"))


class AfreecaTV(Plugin):
    @classmethod
    def can_handle_url(cls, url):
Exemplo n.º 57
0
)
RECORDED_URL = "http://tcdn.ustream.tv/video/{0}"
RTMP_URL = "rtmp://r{0}-1-{1}-channel-live.ums.ustream.tv:1935/ustream"
SWF_URL = "http://static-cdn1.ustream.tv/swf/live/viewer.rsl:505.swf"

_module_info_schema = validate.Schema(
    list,
    validate.length(1),
    validate.get(0),
    dict
)
_amf3_array = validate.Schema(
    validate.any(
        validate.all(
            {int: object},
            validate.transform(lambda a: list(a.values())),
        ),
        list
    )
)
_recorded_schema = validate.Schema({
    validate.optional("stream"): validate.all(
        _amf3_array,
        [{
            "name": validate.text,
            "streams": validate.all(
                _amf3_array,
                [{
                    "streamName": validate.text,
                    "bitrate": float,
                }],
Exemplo n.º 58
0
    http(s)?://(\w+\.)?
    dailymotion.com
    (?:
        (/embed)?/(video|live)
        /(?P<media_id>[^_?/]+)
    |
        /(?P<channel_name>[A-Za-z0-9-_]+)
    )
""", re.VERBOSE)

_media_schema = validate.Schema(validate.any(
    {"error": {"title": validate.text}},
    # "stream_chromecast_url": validate.url(),
    # Chromecast URL is already available in qualities subdict
    {"qualities": validate.any({
        validate.text: validate.all([{
            "type": validate.text,
            "url": validate.url()
        }])
    })
    }))
_live_id_schema = validate.Schema(
    {
        "total": int,
        "list": validate.any(
            [],
            [{"id": validate.text}]
        )
    }
)
Exemplo n.º 59
0
LAPI_URL = "http://g2.live.360.cn/liveplay?stype=flv&channel={}&bid=huajiao&sn={}&sid={}&_rate=xd&ts={}&r={}&_ostype=flash&_delay=0&_sign=null&_ver=13"

_url_re = re.compile(r"""
        http(s)?://(www\.)?huajiao.com
        /l/(?P<channel>[^/]+)
""", re.VERBOSE)

_feed_json_re = re.compile(r'^\s*var\s*feed\s*=\s*(?P<feed>{.*})\s*;', re.MULTILINE)

_feed_json_schema = validate.Schema(
    validate.all(
        validate.transform(_feed_json_re.search),
        validate.any(
            None,
            validate.all(
                validate.get('feed'),
                validate.transform(json.loads)
            )
        )
    )
)


class Huajiao(Plugin):
    @classmethod
    def can_handle_url(self, url):
        return _url_re.match(url)

    def _get_streams(self):
        match = _url_re.match(self.url)
        channel = match.group("channel")
class Dogan(Plugin):
    """
    Support for the live streams from Doğan Media Group channels
    """
    url_re = re.compile(r"""
        https?://(?:www.)?
        (?:teve2.com.tr/(?:canli-yayin|filmler/.*|programlar/.*)|
           kanald.com.tr/.*|
           cnnturk.com/canli-yayin|
           dreamtv.com.tr/canli-yayin|
           dreamturk.com.tr/canli)
    """, re.VERBOSE)
    playerctrl_re = re.compile(r'''<div[^>]*?ng-controller=(?P<quote>["'])(?:Live)?PlayerCtrl(?P=quote).*?>''', re.DOTALL)
    data_id_re = re.compile(r'''data-id=(?P<quote>["'])(?P<id>\w+)(?P=quote)''')
    content_id_re = re.compile(r'"content(?:I|i)d", "(\w+)"')
    item_id_re = re.compile(r"_itemId\s+=\s+'(\w+)';")
    content_api = "/actions/content/media/{id}"
    new_content_api = "/action/media/{id}"
    content_api_schema = validate.Schema({
        "Id": validate.text,
        "Media": {
            "Link": {
                "DefaultServiceUrl": validate.url(),
                validate.optional("ServiceUrl"): validate.any(validate.url(), ""),
                "SecurePath": validate.text,
            }
        }
    })

    @classmethod
    def can_handle_url(cls, url):
        return cls.url_re.match(url) is not None

    def _get_content_id(self):
        res = http.get(self.url)
        # find the contentId
        content_id_m = self.content_id_re.search(res.text)
        if content_id_m:
            self.logger.debug("Found contentId by contentId regex")
            return content_id_m.group(1)

        # find the PlayerCtrl div
        player_ctrl_m = self.playerctrl_re.search(res.text)
        if player_ctrl_m:
            # extract the content id from the player control data
            player_ctrl_div = player_ctrl_m.group(0)
            content_id_m = self.data_id_re.search(player_ctrl_div)
            if content_id_m:
                self.logger.debug("Found contentId by player data-id regex")
                return content_id_m.group("id")

        # find the itemId var
        item_id_m = self.item_id_re.search(res.text)
        if item_id_m:
            self.logger.debug("Found contentId by itemId regex")
            return item_id_m.group(1)

    def _get_hls_url(self, content_id):
        # make the api url relative to the current domain
        if "cnnturk" in self.url or "teve2.com.tr" in self.url:
            self.logger.debug("Using new content API url")
            api_url = urljoin(self.url, self.new_content_api.format(id=content_id))
        else:
            api_url = urljoin(self.url, self.content_api.format(id=content_id))

        apires = http.get(api_url)

        stream_data = http.json(apires, schema=self.content_api_schema)
        d = stream_data["Media"]["Link"]
        return urljoin((d["ServiceUrl"] or d["DefaultServiceUrl"]), d["SecurePath"])

    def _get_streams(self):
        content_id = self._get_content_id()
        if content_id:
            self.logger.debug(u"Loading content: {}", content_id)
            hls_url = self._get_hls_url(content_id)
            return HLSStream.parse_variant_playlist(self.session, hls_url)
        else:
            self.logger.error(u"Could not find the contentId for this stream")