Beispiel #1
0
            int(ts[-6:-5] + "1"))


_url_re = re.compile(
    r"""
    http(s)?://(\w+\.)?crunchyroll\.
    (?:
        com|de|es|fr|co.jp
    )
    (?:/[^/&?]+)?
    /[^/&?]+-(?P<media_id>\d+)
""", re.VERBOSE)

_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")),
Beispiel #2
0
def parse_fmt_list(formatsmap):
    formats = {}
    if not formatsmap:
        return formats

    for format in formatsmap.split(","):
        s = format.split("/")
        (w, h) = s[1].split("x")
        formats[int(s[0])] = "{0}p".format(h)

    return formats


_config_schema = validate.Schema(
    {
        validate.optional("fmt_list"): validate.all(
            validate.text,
            validate.transform(parse_fmt_list)
        ),
        validate.optional("url_encoded_fmt_stream_map"): validate.all(
            validate.text,
            validate.transform(parse_stream_map),
            [{
                "itag": validate.all(
                    validate.text,
                    validate.transform(int)
                ),
                "quality": validate.text,
                "url": validate.url(scheme="http"),
                validate.optional("s"): validate.text,
                validate.optional("stereo3d"): validate.all(
Beispiel #3
0
from streamlink.plugin import Plugin
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:
Beispiel #4
0
_channel_schema = validate.Schema(
    {
        "CHANNEL": {
            "RESULT": validate.transform(int),
            "BROAD_INFOS": [{
                "list": [{
                    "nBroadNo": validate.text
                }]
            }]
        }
    },
    validate.get("CHANNEL")
)
_stream_schema = validate.Schema(
    {
        validate.optional("view_url"): validate.url(
            scheme=validate.any("rtmp", "http")
        )
    }
)


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

    def _get_channel_info(self, username):
        headers = {
            "Referer": self.url
        }
Beispiel #5
0
                            validate.get("Streams")
                        )
                    ],
                    "Server": validate.text
                }],
                validate.filter(lambda s: s["LinkType"] in STREAMING_TYPES)
            )
        }]
    },
    validate.get("Data", {})
)

_video_schema = validate.Schema(
    { "Data": [{
        "Assets": validate.all(
            [{ validate.optional("Links"): validate.all(
                [{
                    "Target": validate.text,
                    "Uri": validate.text
                }],
                validate.filter(lambda l: l["Target"] in STREAMING_TYPES)
            )}],
            validate.filter(lambda a: "Links" in a)
        )
    }]},
    validate.get("Data", {}),
    validate.get(0, {}),
    validate.get("Assets", {}),
    validate.get(0, {}),
    validate.get("Links", []),
)
Beispiel #6
0
def parse_fmt_list(formatsmap):
    formats = {}
    if not formatsmap:
        return formats

    for format in formatsmap.split(","):
        s = format.split("/")
        (w, h) = s[1].split("x")
        formats[int(s[0])] = "{0}p".format(h)

    return formats


_config_schema = validate.Schema(
    {
        validate.optional("fmt_list"): validate.all(
            validate.text,
            validate.transform(parse_fmt_list)
        ),
        validate.optional("url_encoded_fmt_stream_map"): validate.all(
            validate.text,
            validate.transform(parse_stream_map),
            [{
                "itag": validate.all(
                    validate.text,
                    validate.transform(int)
                ),
                "quality": validate.text,
                "url": validate.url(scheme="http"),
                validate.optional("s"): validate.text,
                validate.optional("stereo3d"): validate.all(
Beispiel #7
0
        validate.all(
            validate.get(2),
            validate.url(scheme="http")
        )
    )
)
_video_schema = validate.Schema({
    "videoJsonPlayer": {
        "VSR": validate.any(
            [],
            {
                validate.text: {
                    "height": int,
                    "mediaType": validate.text,
                    "url": validate.text,
                    validate.optional("streamer"): validate.text
                },
            },
        ),
        "VTY": validate.text
    }
})


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

    def _create_stream(self, stream, is_live):
        stream_name = "{0}p".format(stream["height"])
class WWENetwork(Plugin):
    url_re = re.compile(r"https?://network.wwe.com")
    content_id_re = re.compile(r'''"content_id" : "(\d+)"''')
    playback_scenario = "HTTP_CLOUD_WIRED"
    login_url = "https://secure.net.wwe.com/workflow.do"
    login_page_url = "https://secure.net.wwe.com/enterworkflow.do?flowId=account.login&forwardUrl=http%3A%2F%2Fnetwork.wwe.com"
    api_url = "https://ws.media.net.wwe.com/ws/media/mf/op-findUserVerifiedEvent/v-2.3"
    _info_schema = validate.Schema(
        validate.union({
            "status":
            validate.union({
                "code":
                validate.all(validate.xml_findtext(".//status-code"),
                             validate.transform(int)),
                "message":
                validate.xml_findtext(".//status-message"),
            }),
            "urls":
            validate.all(validate.xml_findall(".//url"),
                         [validate.getattr("text")]),
            validate.optional("fingerprint"):
            validate.xml_findtext(".//updated-fingerprint"),
            validate.optional("session_key"):
            validate.xml_findtext(".//session-key"),
            "session_attributes":
            validate.all(validate.xml_findall(".//session-attribute"), [
                validate.getattr("attrib"),
                validate.union({
                    "name": validate.get("name"),
                    "value": validate.get("value")
                })
            ])
        }))
    options = PluginOptions({
        "email": None,
        "password": None,
    })

    def __init__(self, url):
        super(WWENetwork, self).__init__(url)
        http.headers.update({"User-Agent": useragents.CHROME})
        self._session_attributes = Cache(filename="plugin-cache.json",
                                         key_prefix="wwenetwork:attributes")
        self._session_key = self.cache.get("session_key")
        self._authed = self._session_attributes.get(
            "ipid") and self._session_attributes.get("fprt")

    @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 login as {0}", email)
        # sets some required cookies to login
        http.get(self.login_page_url)
        # login
        res = http.post(self.login_url,
                        data=dict(registrationAction='identify',
                                  emailAddress=email,
                                  password=password,
                                  submitButton=""),
                        headers={"Referer": self.login_page_url},
                        allow_redirects=False)

        self._authed = "Authentication Error" not in res.text
        if self._authed:
            self._session_attributes.set("ipid",
                                         res.cookies.get("ipid"),
                                         expires=3600 * 1.5)
            self._session_attributes.set("fprt",
                                         res.cookies.get("fprt"),
                                         expires=3600 * 1.5)

        return self._authed

    def _update_session_attribute(self, key, value):
        if value:
            self._session_attributes.set(key, value,
                                         expires=3600 * 1.5)  # 1h30m expiry
            http.cookies.set(key, value)

    @property
    def session_key(self):
        return self._session_key

    @session_key.setter
    def session_key(self, value):
        self.cache.set("session_key", value)
        self._session_key = value

    def _get_media_info(self, content_id):
        """
        Get the info about the content, based on the ID
        :param content_id:
        :return:
        """
        params = {
            "identityPointId": self._session_attributes.get("ipid"),
            "fingerprint": self._session_attributes.get("fprt"),
            "contentId": content_id,
            "playbackScenario": self.playback_scenario,
            "platform": "WEB_MEDIAPLAYER_5",
            "subject": "LIVE_EVENT_COVERAGE",
            "frameworkURL": "https://ws.media.net.wwe.com",
            "_": int(time.time())
        }
        if self.session_key:
            params["sessionKey"] = self.session_key
        url = self.api_url.format(id=content_id)
        res = http.get(url, params=params)
        return http.xml(res, ignore_ns=True, schema=self._info_schema)

    def _get_content_id(self):
        #  check the page to find the contentId
        res = http.get(self.url)
        m = self.content_id_re.search(res.text)
        if m:
            return m.group(1)

    def _get_streams(self):
        email = self.get_option("email")
        password = self.get_option("password")

        if not self._authed and (not email and not password):
            self.logger.error(
                "A login for WWE Network is required, use --wwenetwork-email/"
                "--wwenetwork-password to set them")
            return

        if not self._authed:
            if not self.login(email, password):
                self.logger.error(
                    "Failed to login, check your username/password")
                return

        content_id = self._get_content_id()
        if content_id:
            self.logger.debug("Found content ID: {0}", content_id)
            info = self._get_media_info(content_id)
            if info["status"]["code"] == 1:
                # update the session attributes
                self._update_session_attribute("fprt", info.get("fingerprint"))
                for attr in info["session_attributes"]:
                    self._update_session_attribute(attr["name"], attr["value"])

                if info.get("session_key"):
                    self.session_key = info.get("session_key")
                for url in info["urls"]:
                    for s in HLSStream.parse_variant_playlist(
                            self.session, url,
                            name_fmt="{pixels}_{bitrate}").items():
                        yield s
            else:
                raise PluginError(
                    "Could not load streams: {message} ({code})".format(
                        **info["status"]))
Beispiel #9
0
class BBCiPlayer(Plugin):
    """
    Allows streaming of live channels from bbc.co.uk/iplayer/live/* and of iPlayer programmes from
    bbc.co.uk/iplayer/episode/*
    """
    mediator_re = re.compile(r'window\.__IPLAYER_REDUX_STATE__\s*=\s*({.*?});',
                             re.DOTALL)
    state_re = re.compile(
        r'window.__IPLAYER_REDUX_STATE__\s*=\s*({.*?});</script>')
    account_locals_re = re.compile(r'window.bbcAccount.locals\s*=\s*({.*?});')
    hash = base64.b64decode(
        b"N2RmZjc2NzFkMGM2OTdmZWRiMWQ5MDVkOWExMjE3MTk5MzhiOTJiZg==")
    api_url = "https://open.live.bbc.co.uk/mediaselector/6/select/version/2.0/mediaset/" \
              "{platform}/vpid/{vpid}/format/json/atk/{vpid_hash}/asn/1/"
    platforms = ("pc", "iptv-all")
    session_url = "https://session.bbc.com/session"
    auth_url = "https://account.bbc.com/signin"

    mediator_schema = validate.Schema({"versions": [{
        "id": validate.text
    }]}, validate.get("versions"), validate.get(0), validate.get("id"))
    mediaselector_schema = validate.Schema(
        validate.transform(parse_json), {
            "media": [{
                "connection":
                validate.all(
                    [{
                        validate.optional("href"): validate.url(),
                        validate.optional("transferFormat"): validate.text
                    }], validate.filter(lambda c: c.get("href"))),
                "kind":
                validate.text
            }]
        }, validate.get("media"),
        validate.filter(lambda x: x["kind"] == "video"))
    arguments = PluginArguments(
        PluginArgument("username",
                       requires=["password"],
                       metavar="USERNAME",
                       help="The username used to register with bbc.co.uk."),
        PluginArgument(
            "password",
            sensitive=True,
            metavar="PASSWORD",
            help=
            "A bbc.co.uk account password to use with --bbciplayer-username.",
            prompt="Enter bbc.co.uk account password"),
        PluginArgument("hd",
                       action="store_true",
                       help="""
            Prefer HD streams over local SD streams, some live programmes may
            not be broadcast in HD.
            """),
    )

    @classmethod
    def _hash_vpid(cls, vpid):
        return sha1(cls.hash + str(vpid).encode("utf8")).hexdigest()

    def find_vpid(self, url, res=None):
        """
        Find the Video Packet ID in the HTML for the provided URL

        :param url: URL to download, if res is not provided.
        :param res: Provide a cached version of the HTTP response to search
        :type url: string
        :type res: requests.Response
        :return: Video Packet ID for a Programme in iPlayer
        :rtype: string
        """
        log.debug(f"Looking for vpid on {url}")
        # Use pre-fetched page if available
        res = res or self.session.http.get(url)
        m = self.mediator_re.search(res.text)
        vpid = m and parse_json(m.group(1), schema=self.mediator_schema)
        return vpid

    def find_tvip(self, url, master=False):
        log.debug("Looking for {0} tvip on {1}".format(
            "master" if master else "", url))
        res = self.session.http.get(url)
        m = self.state_re.search(res.text)
        data = m and parse_json(m.group(1))
        if data:
            channel = data.get("channel")
            if master:
                return channel.get("masterBrand")
            return channel.get("id")

    def mediaselector(self, vpid):
        urls = defaultdict(set)
        for platform in self.platforms:
            url = self.api_url.format(vpid=vpid,
                                      vpid_hash=self._hash_vpid(vpid),
                                      platform=platform)
            log.debug(f"Info API request: {url}")
            medias = self.session.http.get(url,
                                           schema=self.mediaselector_schema)
            for media in medias:
                for connection in media["connection"]:
                    urls[connection.get("transferFormat")].add(
                        connection["href"])

        for stream_type, urls in urls.items():
            log.debug(f"{len(urls)} {stream_type} streams")
            for url in list(urls):
                try:
                    if stream_type == "hds":
                        yield from HDSStream.parse_manifest(self.session,
                                                            url).items()
                    if stream_type == "hls":
                        yield from HLSStream.parse_variant_playlist(
                            self.session, url).items()
                    if stream_type == "dash":
                        yield from DASHStream.parse_manifest(
                            self.session, url).items()
                    log.debug(f"  OK:   {url}")
                except Exception:
                    log.debug(f"  FAIL: {url}")

    def login(self, ptrt_url):
        """
        Create session using BBC ID. See https://www.bbc.co.uk/usingthebbc/account/

        :param ptrt_url: The snapback URL to redirect to after successful authentication
        :type ptrt_url: string
        :return: Whether authentication was successful
        :rtype: bool
        """
        def auth_check(res):
            return ptrt_url in ([h.url for h in res.history] + [res.url])

        # make the session request to get the correct cookies
        session_res = self.session.http.get(self.session_url,
                                            params=dict(ptrt=ptrt_url))

        if auth_check(session_res):
            log.debug("Already authenticated, skipping authentication")
            return True

        res = self.session.http.post(self.auth_url,
                                     params=urlparse(session_res.url).query,
                                     data=dict(
                                         jsEnabled=True,
                                         username=self.get_option("username"),
                                         password=self.get_option('password'),
                                         attempts=0),
                                     headers={"Referer": self.url})

        return auth_check(res)

    def _get_streams(self):
        if not self.get_option("username"):
            log.error("BBC iPlayer requires an account you must login using "
                      "--bbciplayer-username and --bbciplayer-password")
            return
        log.info(
            "A TV License is required to watch BBC iPlayer streams, see the BBC website for more "
            "information: https://www.bbc.co.uk/iplayer/help/tvlicence")
        if not self.login(self.url):
            log.error(
                "Could not authenticate, check your username and password")
            return

        episode_id = self.match.group("episode_id")
        channel_name = self.match.group("channel_name")

        if episode_id:
            log.debug(f"Loading streams for episode: {episode_id}")
            vpid = self.find_vpid(self.url)
            if vpid:
                log.debug(f"Found VPID: {vpid}")
                yield from self.mediaselector(vpid)
            else:
                log.error(f"Could not find VPID for episode {episode_id}")
        elif channel_name:
            log.debug(f"Loading stream for live channel: {channel_name}")
            if self.get_option("hd"):
                tvip = self.find_tvip(self.url, master=True) + "_hd"
                if tvip:
                    log.debug(f"Trying HD stream {tvip}...")
                    try:
                        yield from self.mediaselector(tvip)
                    except PluginError:
                        log.error(
                            "Failed to get HD streams, falling back to SD")
                    else:
                        return
            tvip = self.find_tvip(self.url)
            if tvip:
                log.debug(f"Found TVIP: {tvip}")
                yield from self.mediaselector(tvip)
Beispiel #10
0
class OPENRECtv(Plugin):
    _url_re = re.compile(r"https?://(?:www\.)?openrec.tv/(?:live|movie)/(?P<id>[^/]+)")
    _stores_re = re.compile(r"window.stores\s*=\s*({.*?});", re.DOTALL | re.MULTILINE)
    _config_re = re.compile(r"window.sharedConfig\s*=\s*({.*?});", re.DOTALL | re.MULTILINE)

    api_url = "https://apiv5.openrec.tv/api/v5/movies/{id}/detail"
    login_url = "https://www.openrec.tv/viewapp/v4/mobile/user/login"

    _config_schema = validate.Schema({
        "urls": {
            "apiv5Authorized": validate.url()
        }
    })
    _stores_schema = validate.Schema({
        "moviePageStore": {
            "movieStore": {
                "id": validate.text,
                "title": validate.text,
                "media": {
                    "url": validate.any(None, '', validate.url())
                }
            }
        }
    }, validate.get("moviePageStore"), validate.get("movieStore"))

    _detail_schema = validate.Schema({
        validate.optional("error_message"): validate.text,
        "status": int,
        validate.optional("data"): {
            "type": validate.text,
            "items": [{
                "media": {
                    "url": validate.any(None, validate.url()),
                    "url_dvr": validate.any(None, validate.url())
                }
            }]
        }
    })

    _login_schema = validate.Schema({
        validate.optional("error_message"): validate.text,
        "status": int,
        validate.optional("data"): object
    })

    arguments = PluginArguments(
        PluginArgument(
            "email",
            requires=["password"],
            metavar="EMAIL",
            help="""
            The email associated with your openrectv account,
            required to access any openrectv stream.
            """),
        PluginArgument(
            "password",
            sensitive=True,
            metavar="PASSWORD",
            help="""
            An openrectv account password to use with --openrectv-email.
            """)
    )

    def __init__(self, url):
        super().__init__(url)
        self._pdata = None
        self._pres = None
        self._pconfig = None

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

    def login(self, email, password):
        res = self.session.http.post(self.login_url, data={"mail": email, "password": password})
        data = self.session.http.json(res, self._login_schema)
        if data["status"] == 0:
            log.debug("Logged in as {0}".format(data["data"]["user_name"]))
        else:
            log.error("Failed to login: {0}".format(data["error_message"]))
        return data["status"] == 0

    def _get_page(self):
        if not self._pres:
            self._pres = self.session.http.get(self.url)
        return self._pres

    def _get_movie_data(self):
        pres = self._get_page()
        match = self._stores_re.search(pres.text)
        if match:
            self._pdata = parse_json(match.group(1), schema=self._stores_schema)

        return self._pdata

    def _get_page_config(self):
        pres = self._get_page()
        match = self._config_re.search(pres.text)
        if match:
            self._pconfig = parse_json(match.group(1))

        return self._pconfig

    def _get_details(self, id):
        config = self._get_page_config()
        api_url = config["urls"]["apiv5Authorized"]
        url = "{base}/movies/{id}/detail".format(base=api_url, id=id)
        res = self.session.http.get(url, headers={
            "access-token": self.session.http.cookies.get("access_token"),
            "uuid": self.session.http.cookies.get("uuid")
        })
        data = self.session.http.json(res, schema=self._detail_schema)

        if data["status"] == 0:
            log.debug("Got valid detail response")
            return data["data"]
        else:
            log.error("Failed to get video stream: {0}".format(data["error_message"]))

    def get_title(self):
        mdata = self._get_movie_data()
        if mdata:
            return mdata["title"]

    def _get_streams(self):
        mdata = self._get_movie_data()
        if mdata:
            log.debug("Found video: {0} ({1})".format(mdata["title"], mdata["id"]))
            if mdata["media"]["url"]:
                yield from HLSStream.parse_variant_playlist(self.session, mdata["media"]["url"]).items()
            elif self.get_option("email") and self.get_option("password"):
                if self.login(self.get_option("email"), self.get_option("password")):
                    details = self._get_details(mdata["id"])
                    if details:
                        for item in details["items"]:
                            yield from HLSStream.parse_variant_playlist(self.session, item["media"]["url"]).items()
            else:
                log.error("You must login to access this stream")
Beispiel #11
0
class LivespottingTV(Plugin):
    _player_re = re.compile(r"player_id:\s*'(\w+)',\s*livesource_id:\s*'(\w+)'")

    _URL_PLAYER_CONFIG = "https://player.livespotting.com/v1/config/{player_id}.json"
    _URL_PLAYER_SHOWROOM = "https://player.livespotting.com/v2/livesource/{livesource_id}?type=showroom"

    _playlist_schema = validate.Schema({
        "id": validate.text,
        "playlist": validate.url(scheme="http"),
        "playlist_mode": validate.text,
        "weather_live_enable": bool,
    })
    _sources_schema = validate.Schema([{
        validate.optional("mediaid"): validate.text,
        validate.optional("title"): validate.text,
        validate.optional("livestream"): validate.all(validate.url(scheme="http"), validate.contains(".m3u8")),
        "sources": [{"file": validate.all(validate.url(scheme="http"), validate.contains(".m3u8"))}],
    }])
    _livesource_schema = validate.Schema({
        "id": validate.text,
        "source": validate.all(validate.url(scheme="http"), validate.contains(".m3u8")),
    })

    def _get_streams(self):
        source_id = self.match.group(1)
        res = self.session.http.get(self.url)
        m = self._player_re.search(res.text)
        if m:
            _player_id, _source_id = m.groups()
            if _source_id == source_id:
                config_url = self._URL_PLAYER_CONFIG.format(player_id=_player_id)
                log.debug("config_url: {0}".format(config_url))
                res = self.session.http.get(config_url)
                res = self.session.http.json(res, schema=self._playlist_schema)
                log.debug("playlist_mode: {0}".format(res["playlist_mode"]))
                log.debug("weather_live_enable: {0}".format(res["weather_live_enable"]))
                if res["playlist_mode"] == "showroom":
                    playlist_url = self._URL_PLAYER_SHOWROOM.format(livesource_id=_source_id)
                    _schema = self._livesource_schema
                else:
                    playlist_url = res["playlist"]
                    _schema = self._sources_schema

                log.debug("playlist_url: {0}".format(playlist_url))
                res = self.session.http.get(playlist_url)
                res = self.session.http.json(res, schema=_schema)
                log.trace("sources: {0!r}".format(res))
                for source in res if isinstance(res, list) else [res]:
                    _id = source.get("mediaid") or _source_id
                    if _id == _source_id:
                        title = source.get("title", "N/A")
                        log.debug("title: {0}".format(title))
                        if source.get("livestream"):
                            for s in HLSStream.parse_variant_playlist(self.session, source["livestream"]).items():
                                yield s
                        else:
                            log.debug("No 'livestream' source found, trying alt. sources")
                            sources = source.get("sources") or [{"file": source["source"]}]
                            for file in sources:
                                for s in HLSStream.parse_variant_playlist(self.session, file["file"]).items():
                                    yield s
Beispiel #12
0
class Ceskatelevize(Plugin):

    ajax_url = 'https://www.ceskatelevize.cz/ivysilani/ajax/get-client-playlist'
    _url_re = re.compile(r'http(s)?://([^.]*.)?ceskatelevize.cz')
    _player_re = re.compile(r'ivysilani/embed/iFramePlayer[^"]+')
    _hash_re = re.compile(r'hash:"([0-9a-z]+)"')
    _playlist_info_re = re.compile(r'{"type":"([a-z]+)","id":"([0-9]+)"')
    _playlist_url_schema = validate.Schema({
        validate.optional("streamingProtocol"):
        validate.text,
        "url":
        validate.any(validate.url(), "Error", "error_region")
    })
    _playlist_schema = validate.Schema({
        "playlist": [{
            validate.optional("type"): validate.text,
            "streamUrls": {
                "main": validate.url(),
            }
        }]
    })

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

    def _get_streams(self):
        self.session.http.headers.update({'User-Agent': useragents.IPAD})
        self.session.http.verify = False
        log.warning('SSL certificate verification is disabled.')
        # fetch requested url and find playlist info
        response = self.session.http.get(self.url)
        info = self._find_playlist_info(response)

        if not info:
            # do next try with new API
            def _fallback_api(*args, **kwargs):
                self.api2 = CeskatelevizeAPI2(self.session, self.url, *args,
                                              **kwargs)
                return self.api2._get_streams()

            # playlist info not found, let's try to find player url
            player_url = self._find_player_url(response)
            if not player_url:
                log.debug(
                    'Cannot find playlist info or player url, do next try with new API'
                )
                return _fallback_api(res=response)

            # get player url and try to find playlist info in it
            response = self.session.http.get(player_url)
            info = self._find_playlist_info(response)
            if not info:
                log.debug(
                    'Cannot find playlist info in the player url, do next try with new API'
                )
                return _fallback_api()

        log.trace('{0!r}'.format(info))

        data = {
            'playlist[0][type]': info['type'],
            'playlist[0][id]': info['id'],
            'requestUrl': '/ivysilani/embed/iFramePlayer.php',
            'requestSource': 'iVysilani',
            'type': 'html'
        }
        headers = {
            'x-addr': '127.0.0.1',
        }

        # fetch playlist url
        response = self.session.http.post(self.ajax_url,
                                          data=data,
                                          headers=headers)
        json_data = self.session.http.json(response,
                                           schema=self._playlist_url_schema)
        log.trace('{0!r}'.format(json_data))

        if json_data['url'] in ['Error', 'error_region']:
            log.error('This stream is not available')
            return

        # fetch playlist
        response = self.session.http.post(json_data['url'])
        json_data = self.session.http.json(response,
                                           schema=self._playlist_schema)
        log.trace('{0!r}'.format(json_data))
        playlist = json_data['playlist'][0]['streamUrls']['main']
        return HLSStream.parse_variant_playlist(self.session, playlist)

    @classmethod
    def _find_playlist_info(cls, response):
        """
        Finds playlist info (type, id) in HTTP response.

        :param response: Response object.
        :returns: Dictionary with type and id.
        """
        values = {}
        matches = cls._playlist_info_re.search(response.text)
        if matches:
            values['type'] = matches.group(1)
            values['id'] = matches.group(2)

        return values

    @classmethod
    def _find_player_url(cls, response):
        """
        Finds embedded player url in HTTP response.

        :param response: Response object.
        :returns: Player url (str).
        """
        url = ''
        matches = cls._player_re.search(response.text)
        if matches:
            tmp_url = matches.group(0).replace('&amp;', '&')
            if 'hash' not in tmp_url:
                # there's no hash in the URL, try to find it
                matches = cls._hash_re.search(response.text)
                if matches:
                    url = tmp_url + '&hash=' + matches.group(1)
            else:
                url = tmp_url

        return 'http://ceskatelevize.cz/' + url
Beispiel #13
0
class CeskatelevizeAPI2:
    _player_api = 'https://playlist.ceskatelevize.cz/'
    _url_re = re.compile(r'http(s)?://([^.]*.)?ceskatelevize.cz')
    _playlist_info_re = re.compile(
        r'{\s*"type":\s*"([a-z]+)",\s*"id":\s*"(\w+)"')
    _playlist_schema = validate.Schema({
        "CODE": validate.contains("OK"),
        "RESULT": {
            "playlist": [{
                "streamUrls": {
                    "main": validate.url(),
                }
            }]
        }
    })
    _ctcomp_re = re.compile(
        r'data-ctcomp="Video"\sdata-video-id="(?P<val1>[^"]*)"\sdata-ctcomp-data="(?P<val2>[^"]+)">'
    )
    _ctcomp_schema = validate.Schema(
        validate.text, validate.transform(_ctcomp_re.findall),
        validate.transform(
            lambda vl: [{
                "video-id": v[0],
                "ctcomp-data": json.loads(html_unescape(v[1]))
            } for v in vl]))
    _playlist_info_schema = validate.Schema({
        "type":
        validate.text,
        "id":
        validate.any(validate.text, int),
        "key":
        validate.text,
        "date":
        validate.text,
        "requestSource":
        validate.text,
        "drm":
        int,
        validate.optional("canBePlay"):
        int,
        validate.optional("assetId"):
        validate.text,
        "quality":
        validate.text,
        validate.optional("region"):
        int
    })

    def __init__(self, session, url, res=None):
        self.session = session
        self.url = url
        self.response = res

    def _get_streams(self):
        if self.response is None:
            infos = self.session.http.get(self.url, schema=self._ctcomp_schema)
        else:
            infos = self.session.http.json(self.response,
                                           schema=self._ctcomp_schema)
        if not infos:
            # playlist infos not found
            raise PluginError('Cannot find playlist infos!')

        vod_prio = len(infos) == 2
        for info in infos:
            try:
                pl = info['ctcomp-data']['source']['playlist'][0]
            except KeyError:
                raise PluginError('Cannot find playlist info!')

            pl = self._playlist_info_schema.validate(pl)
            if vod_prio and pl['type'] != 'VOD':
                continue

            log.trace('{0!r}'.format(info))
            if pl['type'] == 'LIVE':
                data = {
                    "contentType":
                    "live",
                    "items": [{
                        "id": pl["id"],
                        "assetId": pl["assetId"],
                        "key": pl["key"],
                        "playerType": "dash",
                        "date": pl["date"],
                        "requestSource": pl["requestSource"],
                        "drm": pl["drm"],
                        "quality": pl["quality"],
                    }]
                }
            elif pl['type'] == 'VOD':
                data = {
                    "contentType":
                    "vod",
                    "items": [{
                        "id": pl["id"],
                        "key": pl["key"],
                        "playerType": "dash",
                        "date": pl["date"],
                        "requestSource": pl["requestSource"],
                        "drm": pl["drm"],
                        "canBePlay": pl["canBePlay"],
                        "quality": pl["quality"],
                        "region": pl["region"]
                    }]
                }

        headers = {
            "Content-Type": "application/x-www-form-urlencoded; charset=UTF-8",
        }

        data = json.dumps(data)
        response = self.session.http.post(self._player_api,
                                          data="data={}".format(quote(data)),
                                          headers=headers)
        json_data = self.session.http.json(response,
                                           schema=self._playlist_schema)
        log.trace('{0!r}'.format(json_data))
        playlist = json_data['RESULT']['playlist'][0]['streamUrls']['main']
        yield from DASHStream.parse_manifest(self.session, playlist).items()
class IDF1(Plugin):
    DACAST_API_URL = 'https://json.dacast.com/b/{}/{}/{}'
    DACAST_TOKEN_URL = 'https://services.dacast.com/token/i/b/{}/{}/{}'

    _url_re = re.compile(r'http://www\.idf1\.fr/(videos/[^/]+/[^/]+\.html|live\b)')
    _video_id_re = re.compile(r"dacast\('(?P<broadcaster_id>\d+)_(?P<video_type>[a-z]+)_(?P<video_id>\d+)', 'replay_content', data\);")
    _video_id_alt_re = re.compile(r'<script src="https://player.dacast.com/js/player.js" id="(?P<broadcaster_id>\d+)_(?P<video_type>[cf])_(?P<video_id>\d+)"')
    _player_url = 'http://ssl.p.jwpcdn.com/player/v/7.12.6/jwplayer.flash.swf'

    _api_schema = validate.Schema(
        validate.transform(parse_json),
        {
            validate.optional('html5'): validate.all(
                [
                    {
                        'src': validate.url()
                    },
                ],
            ),
            'hls': validate.url(),
            'hds': validate.url()
        },
        validate.transform(lambda x: [update_scheme(IDF1.DACAST_API_URL, x['hls']), x['hds']] + [y['src'] for y in x.get('html5', [])])
    )

    _token_schema = validate.Schema(
        validate.transform(parse_json),
        {'token': validate.text},
        validate.get('token')
    )

    _user_agent = useragents.IE_11

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

    def _get_streams(self):
        res = self.session.http.get(self.url)
        match = self._video_id_re.search(res.text) or self._video_id_alt_re.search(res.text)
        if match is None:
            return
        broadcaster_id = match.group('broadcaster_id')
        video_type = match.group('video_type')
        video_id = match.group('video_id')

        videos = self.session.http.get(self.DACAST_API_URL.format(broadcaster_id, video_type, video_id), schema=self._api_schema)
        token = self.session.http.get(
            self.DACAST_TOKEN_URL.format(broadcaster_id, video_type, video_id),
            schema=self._token_schema,
            headers={'referer': self.url}
        )
        parsed = []

        for video_url in videos:
            video_url += token

            # Ignore duplicate video URLs
            if video_url in parsed:
                continue
            parsed.append(video_url)

            # Ignore HDS streams (broken)
            if '.m3u8' in video_url:
                for s in HLSStream.parse_variant_playlist(self.session, video_url).items():
                    yield s
Beispiel #15
0
    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(
    {
        validate.optional("playurl"): validate.url(scheme="http")
    },
    validate.get("playurl")
)

STREAM_WEIGHTS = {
    "middle": 540,
    "source": 1080
}

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

    @classmethod
Beispiel #16
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)
Beispiel #17
0
from streamlink.exceptions import PluginError

_url_re = re.compile(
    r'http(s)?://([^.]*.)?ceskatelevize.cz'
)
_player_re = re.compile(
    r'ivysilani/embed/iFramePlayer[^"]+'
)
_hash_re = re.compile(
    r'hash:"([0-9a-z]+)"'
)
_playlist_info_re = re.compile(
    r'{"type":"([a-z]+)","id":"([0-9]+)"'
)
_playlist_url_schema = validate.Schema({
    validate.optional("streamingProtocol"): validate.text,
    "url": validate.any(
        validate.url(),
        "Error",
        "error_region"
    )
})
_playlist_schema = validate.Schema({
    "playlist": [{
        validate.optional("type"): validate.text,
        "streamUrls": {
            "main": validate.url(),
        }
    }]
})
Beispiel #18
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",
    "低画質": "low",
    "720": 3,
    "480": 2,
    "medium": 2,
    "360": 1,
    "low": 1
}

_url_re = re.compile("""
    http(s)?://(\w+\.)?gaminglive\.tv
    /(?P<type>channels|videos)/(?P<name>[^/]+)
""", re.VERBOSE)
_quality_re = re.compile("[^/]+-(?P<quality>[^/]+)")

_channel_schema = validate.Schema(
    {
        validate.optional("state"): {
            "stream": {
                "qualities": [validate.text],
                "rootUrl": validate.url(scheme="rtmp")
            }
        }
    },
    validate.get("state")
)

_vod_schema = validate.Schema(
    {
        "name": validate.text,
        "channel_slug": validate.text,
        "title": validate.text,
        "created_at": validate.transform(int)
Beispiel #20
0
class BBCiPlayer(Plugin):
    """
    Allows streaming of live channels from bbc.co.uk/iplayer/live/* and of iPlayer programmes from
    bbc.co.uk/iplayer/episode/*
    """
    url_re = re.compile(r"""https?://(?:www\.)?bbc.co.uk/iplayer/
        (
            episode/(?P<episode_id>\w+)|
            live/(?P<channel_name>\w+)
        )
    """, re.VERBOSE)
    mediator_re = re.compile(
        r'window\.mediatorDefer\s*=\s*page\([^,]*,\s*({.*?})\);', re.DOTALL)
    tvip_re = re.compile(r'channel"\s*:\s*{\s*"id"\s*:\s*"(\w+?)"')
    tvip_master_re = re.compile(r'event_master_brand=(\w+?)&')
    account_locals_re = re.compile(r'window.bbcAccount.locals\s*=\s*({.*?});')
    swf_url = "http://emp.bbci.co.uk/emp/SMPf/1.18.3/StandardMediaPlayerChromelessFlash.swf"
    hash = base64.b64decode(
        b"N2RmZjc2NzFkMGM2OTdmZWRiMWQ5MDVkOWExMjE3MTk5MzhiOTJiZg==")
    api_url = ("http://open.live.bbc.co.uk/mediaselector/6/select/"
               "version/2.0/mediaset/{platform}/vpid/{vpid}/format/json/atk/{vpid_hash}/asn/1/")
    platforms = ("pc", "iptv-all")
    session_url = "https://session.bbc.com/session"
    auth_url = "https://account.bbc.com/signin"

    mediator_schema = validate.Schema(
        {
            "appStoreState": {
                "versions": [{"id": validate.text}]
            }
        },
        validate.get("appStoreState"), validate.get("versions"), validate.get(0),
        validate.get("id")
    )
    mediaselector_schema = validate.Schema(
        validate.transform(parse_json),
        {"media": [
            {"connection": [{
                validate.optional("href"): validate.url(),
                validate.optional("transferFormat"): validate.text
            }],
                "kind": validate.text}
        ]},
        validate.get("media"),
        validate.filter(lambda x: x["kind"] == "video")
    )
    arguments = PluginArguments(
        PluginArgument(
            "username",
            requires=["password"],
            metavar="USERNAME",
            help="The username used to register with bbc.co.uk."
        ),
        PluginArgument(
            "password",
            sensitive=True,
            metavar="PASSWORD",
            help="A bbc.co.uk account password to use with --bbciplayer-username.",
            prompt="Enter bbc.co.uk account password"
        ),
        PluginArgument(
            "hd",
            action="store_true",
            help="""
            Prefer HD streams over local SD streams, some live programmes may
            not be broadcast in HD.
            """
        ),
    )

    @classmethod
    def can_handle_url(cls, url):
        """ Confirm plugin can handle URL """
        return cls.url_re.match(url) is not None

    @classmethod
    def _hash_vpid(cls, vpid):
        return sha1(cls.hash + str(vpid).encode("utf8")).hexdigest()

    @classmethod
    def _extract_nonce(cls, http_result):
        """
        Given an HTTP response from the sessino endpoint, extract the nonce, so we can "sign" requests with it.
        We don't really sign the requests in the traditional sense of a nonce, we just incude them in the auth requests.

        :param http_result: HTTP response from the bbc session endpoint.
        :type http_result: requests.Response
        :return: nonce to "sign" url requests with
        :rtype: string
        """

        # Extract the redirect URL from the last call
        last_redirect_url = urlparse(http_result.history[-1].request.url)
        last_redirect_query = dict(parse_qsl(last_redirect_url.query))
        # Extract the nonce from the query string in the redirect URL
        final_url = urlparse(last_redirect_query['goto'])
        goto_url = dict(parse_qsl(final_url.query))
        goto_url_query = parse_json(goto_url['state'])

        # Return the nonce we can use for future queries
        return goto_url_query['nonce']

    def find_vpid(self, url, res=None):
        """
        Find the Video Packet ID in the HTML for the provided URL

        :param url: URL to download, if res is not provided.
        :param res: Provide a cached version of the HTTP response to search
        :type url: string
        :type res: requests.Response
        :return: Video Packet ID for a Programme in iPlayer
        :rtype: string
        """
        log.debug("Looking for vpid on {0}", url)
        # Use pre-fetched page if available
        res = res or self.session.http.get(url)
        m = self.mediator_re.search(res.text)
        vpid = m and parse_json(m.group(1), schema=self.mediator_schema)
        return vpid

    def find_tvip(self, url, master=False):
        log.debug("Looking for {0} tvip on {1}", "master" if master else "", url)
        res = self.session.http.get(url)
        if master:
            m = self.tvip_master_re.search(res.text)
        else:
            m = self.tvip_re.search(res.text)
        return m and m.group(1)

    def mediaselector(self, vpid):
        urls = defaultdict(set)
        for platform in self.platforms:
            url = self.api_url.format(vpid=vpid, vpid_hash=self._hash_vpid(vpid),
                                      platform=platform)
            log.debug("Info API request: {0}", url)
            medias = self.session.http.get(url, schema=self.mediaselector_schema)
            for media in medias:
                for connection in media["connection"]:
                    urls[connection.get("transferFormat")].add(connection["href"])

        for stream_type, urls in urls.items():
            log.debug("{0} {1} streams", len(urls), stream_type)
            for url in list(urls):
                try:
                    if stream_type == "hds":
                        for s in HDSStream.parse_manifest(self.session,
                                                          url).items():
                            yield s
                    if stream_type == "hls":
                        for s in HLSStream.parse_variant_playlist(self.session,
                                                                  url).items():
                            yield s
                    if stream_type == "dash":
                        for s in DASHStream.parse_manifest(self.session,
                                                           url).items():
                            yield s
                    log.debug("  OK:   {0}", url)
                except:
                    log.debug("  FAIL: {0}", url)

    def login(self, ptrt_url):
        """
        Create session using BBC ID. See https://www.bbc.co.uk/usingthebbc/account/

        :param ptrt_url: The snapback URL to redirect to after successful authentication
        :type ptrt_url: string
        :return: Whether authentication was successful
        :rtype: bool
        """
        def auth_check(res):
            return ptrt_url in ([h.url for h in res.history] + [res.url])

        # make the session request to get the correct cookies
        session_res = self.session.http.get(
            self.session_url,
            params=dict(ptrt=ptrt_url)
        )

        if auth_check(session_res):
            log.debug("Already authenticated, skipping authentication")
            return True

        http_nonce = self._extract_nonce(session_res)
        res = self.session.http.post(
            self.auth_url,
            params=dict(
                ptrt=ptrt_url,
                nonce=http_nonce
            ),
            data=dict(
                jsEnabled=True,
                username=self.get_option("username"),
                password=self.get_option('password'),
                attempts=0
            ),
            headers={"Referer": self.url})

        return auth_check(res)

    def _get_streams(self):
        if not self.get_option("username"):
            log.error(
                "BBC iPlayer requires an account you must login using "
                "--bbciplayer-username and --bbciplayer-password")
            return
        log.info(
            "A TV License is required to watch BBC iPlayer streams, see the BBC website for more "
            "information: https://www.bbc.co.uk/iplayer/help/tvlicence")
        if not self.login(self.url):
            log.error(
                "Could not authenticate, check your username and password")
            return

        m = self.url_re.match(self.url)
        episode_id = m.group("episode_id")
        channel_name = m.group("channel_name")

        if episode_id:
            log.debug("Loading streams for episode: {0}", episode_id)
            vpid = self.find_vpid(self.url)
            if vpid:
                log.debug("Found VPID: {0}", vpid)
                for s in self.mediaselector(vpid):
                    yield s
            else:
                log.error("Could not find VPID for episode {0}",
                          episode_id)
        elif channel_name:
            log.debug("Loading stream for live channel: {0}", channel_name)
            if self.get_option("hd"):
                tvip = self.find_tvip(self.url, master=True) + "_hd"
                if tvip:
                    log.debug("Trying HD stream {0}...", tvip)
                    try:
                        for s in self.mediaselector(tvip):
                            yield s
                    except PluginError:
                        log.error(
                            "Failed to get HD streams, falling back to SD")
                    else:
                        return
            tvip = self.find_tvip(self.url)
            if tvip:
                log.debug("Found TVIP: {0}", tvip)
                for s in self.mediaselector(tvip):
                    yield s
Beispiel #21
0
    )
''', re.VERBOSE)

_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",
Beispiel #22
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'

    _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

            '''))

    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'])
Beispiel #23
0
    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
            }]
        }],
        "plugins": validate.all(
            dict,
            validate.filter(lambda k, v: k in ["rtmp", "rtmpHitbox", "hls"]),
            {
                validate.text: {
                    validate.optional("netConnectionUrl"): validate.text,
                    "url": validate.text
Beispiel #24
0
def parse_fmt_list(formatsmap):
    formats = {}
    if not formatsmap:
        return formats

    for format in formatsmap.split(","):
        s = format.split("/")
        (w, h) = s[1].split("x")
        formats[int(s[0])] = "{0}p".format(h)

    return formats


_config_schema = validate.Schema({
    validate.optional("fmt_list"):
    validate.all(validate.text, validate.transform(parse_fmt_list)),
    validate.optional("url_encoded_fmt_stream_map"):
    validate.all(validate.text, validate.transform(parse_stream_map), [{
        "itag":
        validate.all(validate.text, validate.transform(int)),
        "quality":
        validate.text,
        "url":
        validate.url(scheme="http"),
        validate.optional("s"):
        validate.text,
        validate.optional("stereo3d"):
        validate.all(validate.text, validate.transform(int),
                     validate.transform(bool)),
    }]),
Beispiel #25
0
        int(ts[-6:-5] + "1")
    )


_url_re = re.compile(r"""
    http(s)?://(\w+\.)?crunchyroll\.
    (?:
        com|de|es|fr|co.jp
    )
    (?:/[^/&?]+)?
    /[^/&?]+-(?P<media_id>\d+)
""", re.VERBOSE)

_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")
                        ),
Beispiel #26
0
class LiveEdu(Plugin):
    login_url = "https://www.liveedu.tv/accounts/login/"
    url_re = re.compile(r"https?://(?:\w+\.)?(?:livecoding|liveedu)\.tv/")
    config_re = re.compile(
        r"""\Wconfig.(?P<key>\w+)\s*=\s*(?P<q>['"])(?P<value>.*?)(?P=q);""")
    csrf_re = re.compile(r'''"csrfToken"\s*:\s*"(\w+)"''')
    api_schema = validate.Schema({
        "viewing_urls": {
            validate.optional("error"):
            validate.text,
            validate.optional("urls"): [{
                "src":
                validate.url(),
                "type":
                validate.text,
                validate.optional("res"):
                int,
                validate.optional("label"):
                validate.text,
            }]
        }
    })
    config_schema = validate.Schema({
        "selectedVideoHID": validate.text,
        "livestreamURL": validate.text,
        "videosURL": validate.text
    })

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

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

    def login(self):
        """
        Attempt a login to LiveEdu.tv
        """
        email = self.get_option("email")
        password = self.get_option("password")

        if email and password:
            res = http.get(self.login_url)
            csrf_match = self.csrf_re.search(res.text)
            token = csrf_match and csrf_match.group(1)
            self.logger.debug("Attempting login as {0} (token={1})", email,
                              token)

            res = http.post(self.login_url,
                            data=dict(login=email,
                                      password=password,
                                      csrfmiddlewaretoken=token),
                            allow_redirects=False,
                            raise_for_status=False,
                            headers={"Referer": self.login_url})

            if res.status_code != 302:
                self.logger.error("Failed to login to LiveEdu account: {0}",
                                  email)

    def _get_streams(self):
        """
        Get the config object from the page source and call the
        API to get the list of streams
        :return:
        """
        # attempt a login
        self.login()

        res = http.get(self.url)
        # decode the config for the page
        matches = self.config_re.finditer(res.text)
        try:
            config = self.config_schema.validate(
                dict([m.group("key", "value") for m in matches]))
        except PluginError:
            return

        if config["selectedVideoHID"]:
            self.logger.debug("Found video hash ID: {0}",
                              config["selectedVideoHID"])
            api_url = urljoin(
                self.url,
                urljoin(config["videosURL"], config["selectedVideoHID"]))
        elif config["livestreamURL"]:
            self.logger.debug("Found live stream URL: {0}",
                              config["livestreamURL"])
            api_url = urljoin(self.url, config["livestreamURL"])
        else:
            return

        ares = http.get(api_url)
        data = http.json(ares, schema=self.api_schema)
        viewing_urls = data["viewing_urls"]

        if "error" in viewing_urls:
            self.logger.error("Failed to load streams: {0}",
                              viewing_urls["error"])
        else:
            for url in viewing_urls["urls"]:
                try:
                    label = "{0}p".format(url.get("res", url["label"]))
                except KeyError:
                    label = "live"

                if url["type"] == "rtmp/mp4" and RTMPStream.is_usable(
                        self.session):
                    params = {
                        "rtmp": url["src"],
                        "pageUrl": self.url,
                        "live": True,
                    }
                    yield label, RTMPStream(self.session, params)

                elif url["type"] == "application/x-mpegURL":
                    for s in HLSStream.parse_variant_playlist(
                            self.session, url["src"]).items():
                        yield s
Beispiel #27
0
_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)
_flashvar_re = re.compile(r"""(['"])(.*?)\1\s*:\s*(['"])(.*?)\3""")
_clientlibs_re = re.compile(r"""<script.*?src=(['"])(.*?/clientlibs_anime_watch.*?\.js)\1""")

_schema = validate.Schema(
    validate.union({
        "flashvars": validate.all(
            validate.transform(_flashvars_re.search),
            validate.get(1),
            validate.transform(_flashvar_re.findall),
            validate.map(lambda v: (v[1], v[3])),
            validate.transform(dict),
            {
                "s": validate.text,
                "country": validate.text,
                "init": validate.text,
                validate.optional("ss_id"): validate.text,
                validate.optional("mv_id"): validate.text,
                validate.optional("device_cd"): validate.text,
                validate.optional("ss1_prm"): validate.text,
                validate.optional("ss2_prm"): validate.text,
                validate.optional("ss3_prm"): validate.text
            }
        ),
        "clientlibs": validate.all(
            validate.transform(_clientlibs_re.search),
            validate.get(2),
            validate.text
        )
    })
)
    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(
        {
            "base": [validate.text]
        },
        validate.get("base"),
    )
})


class Livestation(Plugin):
    options = PluginOptions({
        "email": "",
        "password": ""
    })

    @classmethod
Beispiel #30
0
class Vimeo(Plugin):
    _config_url_re = re.compile(
        r'(?:"config_url"|\bdata-config-url)\s*[:=]\s*(".+?")')
    _config_re = re.compile(r"var\s+config\s*=\s*({.+?})\s*;")
    _config_url_schema = validate.Schema(
        validate.transform(_config_url_re.search),
        validate.any(
            None,
            validate.Schema(
                validate.get(1),
                validate.parse_json(),
                validate.transform(html_unescape),
                validate.url(),
            ),
        ),
    )
    _config_schema = validate.Schema(
        validate.parse_json(),
        {
            "request": {
                "files": {
                    validate.optional("dash"): {
                        "cdns": {
                            validate.text: {
                                "url": validate.url()
                            }
                        }
                    },
                    validate.optional("hls"): {
                        "cdns": {
                            validate.text: {
                                "url": validate.url()
                            }
                        }
                    },
                    validate.optional("progressive"):
                    validate.all([{
                        "url": validate.url(),
                        "quality": validate.text
                    }]),
                },
                validate.optional("text_tracks"):
                validate.all([{
                    "url": validate.text,
                    "lang": validate.text
                }]),
            }
        },
    )
    _player_schema = validate.Schema(
        validate.transform(_config_re.search),
        validate.any(None, validate.Schema(validate.get(1), _config_schema)),
    )

    arguments = PluginArguments(PluginArgument("mux-subtitles",
                                               is_global=True))

    def _get_streams(self):
        if "player.vimeo.com" in self.url:
            data = self.session.http.get(self.url, schema=self._player_schema)
        else:
            api_url = self.session.http.get(self.url,
                                            schema=self._config_url_schema)
            if not api_url:
                return
            data = self.session.http.get(api_url, schema=self._config_schema)

        videos = data["request"]["files"]
        streams = []

        for stream_type in ("hls", "dash"):
            if stream_type not in videos:
                continue
            for _, video_data in videos[stream_type]["cdns"].items():
                log.trace("{0!r}".format(video_data))
                url = video_data.get("url")
                if stream_type == "hls":
                    for stream in HLSStream.parse_variant_playlist(
                            self.session, url).items():
                        streams.append(stream)
                elif stream_type == "dash":
                    p = urlparse(url)
                    if p.path.endswith("dash.mpd"):
                        # LIVE
                        url = self.session.http.get(url).json()["url"]
                    elif p.path.endswith("master.json"):
                        # VOD
                        url = url.replace("master.json", "master.mpd")
                    else:
                        log.error("Unsupported DASH path: {0}".format(p.path))
                        continue

                    for stream in DASHStream.parse_manifest(self.session,
                                                            url).items():
                        streams.append(stream)

        for stream in videos.get("progressive", []):
            streams.append(
                (stream["quality"], HTTPStream(self.session, stream["url"])))

        if self.get_option("mux_subtitles") and data["request"].get(
                "text_tracks"):
            substreams = {
                s["lang"]: HTTPStream(self.session,
                                      "https://vimeo.com" + s["url"])
                for s in data["request"]["text_tracks"]
            }
            for quality, stream in streams:
                yield quality, MuxedStream(self.session,
                                           stream,
                                           subtitles=substreams)
        else:
            for stream in streams:
                yield stream
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)
Beispiel #32
0
_plu_schema = validate.Schema({
    "playLines": [{
        "urls": [{
            "securityUrl":
            validate.url(scheme=validate.any("rtmp", "http")),
            "resolution":
            validate.text,
            "ext":
            validate.text
        }]
    }]
})

_qq_schema = validate.Schema(
    {validate.optional("playurl"): validate.url(scheme="http")},
    validate.get("playurl"))

STREAM_WEIGHTS = {"middle": 540, "source": 1080}


class Tga(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], "tga"
Beispiel #33
0
import re

from random import random

from streamlink.plugin import Plugin, PluginError
from streamlink.plugin.api import http, validate
from streamlink.stream import HTTPStream, RTMPStream

API_CLIENT_NAME = "Bambuser AS2"
API_CONTEXT = "b_broadcastpage"
API_KEY = "005f64509e19a868399060af746a00aa"
API_URL_VIDEO = "http://player-c.api.bambuser.com/getVideo.json"

_url_re = re.compile("http(s)?://(\w+.)?bambuser.com/v/(?P<video_id>\d+)")
_video_schema = validate.Schema({
    validate.optional("error"): validate.text,
    validate.optional("result"): {
        "id": validate.text,
        "size": validate.text,
        "url": validate.url(
            scheme=validate.any("rtmp", "http")
        )
    }
})


class Bambuser(Plugin):
    @classmethod
    def can_handle_url(self, url):
        return _url_re.match(url)
Beispiel #34
0
def parse_fmt_list(formatsmap):
    formats = {}
    if not formatsmap:
        return formats

    for format in formatsmap.split(","):
        s = format.split("/")
        (w, h) = s[1].split("x")
        formats[int(s[0])] = "{0}p".format(h)

    return formats


_config_schema = validate.Schema(
    {
        validate.optional("fmt_list"): validate.all(
            validate.text,
            validate.transform(parse_fmt_list)
        ),
        validate.optional("url_encoded_fmt_stream_map"): validate.all(
            validate.text,
            validate.transform(parse_stream_map),
            [{
                "itag": validate.all(
                    validate.text,
                    validate.transform(int)
                ),
                "quality": validate.text,
                "url": validate.url(scheme="http"),
                validate.optional("s"): validate.text,
                validate.optional("stereo3d"): validate.all(
Beispiel #35
0
 def test_dict_optional_keys(self):
     assert validate({"a": 1, optional("b"): 2}, {"a": 1}) == {"a": 1}
     assert validate({"a": 1, optional("b"): 2},
                     {"a": 1, "b": 2}) == {"a": 1, "b": 2}
Beispiel #36
0
QUALITY_WEIGHTS = {
    "aws_original": 1080,
    "aws_hd": 720,
    "aws_sd": 480,
}

CDN_SITES = {"aws": "aws_cf"}

_url_re = re.compile(
    r"http(s)?://(?P<cdn>\w+\.)?afreeca(tv)?.com/(?P<username>\w+)(/\d+)?")

_channel_schema = validate.Schema(
    {
        "CHANNEL": {
            "RESULT": validate.transform(int),
            validate.optional("BNO"): validate.text,
            validate.optional("RMD"): validate.text,
            validate.optional("AID"): 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):
    @classmethod
        int(ts[-6:-5] + "1")
    )


_url_re = re.compile("""
    http(s)?://(\w+\.)?crunchyroll\.
    (?:
        com|de|es|fr|co.jp
    )
    /[^/&?]+
    /[^/&?]+-(?P<media_id>\d+)
""", re.VERBOSE)

_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")
                        )
Beispiel #38
0
class Schoolism(Plugin):
    url_re = re.compile(
        r"https?://(?:www\.)?schoolism\.com/(viewAssignment|watchLesson).php")
    login_url = "https://www.schoolism.com/index.php"
    key_time_url = "https://www.schoolism.com/video-html/key-time.php"
    playlist_re = re.compile(r"var allVideos\s*=\s*(\[.*\]);", re.DOTALL)
    js_to_json = partial(re.compile(r'(?!<")(\w+):(?!/)').sub, r'"\1":')
    fix_brackets = partial(re.compile(r',\s*\}').sub, r'}')
    playlist_schema = validate.Schema(
        validate.transform(playlist_re.search),
        validate.any(
            None,
            validate.all(
                validate.get(1),
                validate.transform(js_to_json),
                validate.transform(fix_brackets),  # remove invalid ,
                validate.transform(parse_json),
                [{
                    "sources":
                    validate.all(
                        [{
                            validate.optional("playlistTitle"): validate.text,
                            "title": validate.text,
                            "src": validate.text,
                            "type": validate.text,
                        }],
                        # only include HLS streams
                        # validate.filter(lambda s: s["type"] == "application/x-mpegurl")
                    )
                }])))

    arguments = PluginArguments(
        PluginArgument("email",
                       required=True,
                       requires=["password"],
                       metavar="EMAIL",
                       help="""
        The email associated with your Schoolism account,
        required to access any Schoolism stream.
        """),
        PluginArgument(
            "password",
            sensitive=True,
            metavar="PASSWORD",
            help="A Schoolism account password to use with --schoolism-email."
        ),
        PluginArgument("part",
                       type=int,
                       default=1,
                       metavar="PART",
                       help="""
        Play part number PART of the lesson, or assignment feedback video.

        Defaults is 1.
        """))

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

    def login(self, email, password):
        """
        Login to the schoolism account and return the users account
        :param email: (str) email for account
        :param password: (str) password for account
        :return: (str) users email
        """
        if self.options.get("email") and self.options.get("password"):
            res = self.session.http.post(self.login_url,
                                         data={
                                             "email": email,
                                             "password": password,
                                             "redirect": None,
                                             "submit": "Login"
                                         })

            if res.cookies.get("password") and res.cookies.get("email"):
                return res.cookies.get("email")
            else:
                log.error(
                    "Failed to login to Schoolism, incorrect email/password combination"
                )
        else:
            log.error(
                "An email and password are required to access Schoolism streams"
            )

    def _get_streams(self):
        user = self.login(self.options.get("email"),
                          self.options.get("password"))
        if user:
            log.debug(f"Logged in to Schoolism as {user}")
            res = self.session.http.get(
                self.url, headers={"User-Agent": useragents.SAFARI_8})
            lesson_playlist = self.playlist_schema.validate(res.text)

            part = self.options.get("part")
            video_type = "Lesson" if "lesson" in self.url_re.match(
                self.url).group(1).lower() else "Assignment Feedback"

            log.info(f"Attempting to play {video_type} Part {part}")
            found = False

            # make request to key-time api, to get key specific headers
            _ = self.session.http.get(
                self.key_time_url, headers={"User-Agent": useragents.SAFARI_8})

            for i, video in enumerate(lesson_playlist, 1):
                if video["sources"] and i == part:
                    found = True
                    for source in video["sources"]:
                        if source['type'] == "video/mp4":
                            yield "live", HTTPStream(self.session,
                                                     source["src"],
                                                     headers={
                                                         "User-Agent":
                                                         useragents.SAFARI_8,
                                                         "Referer": self.url
                                                     })
                        elif source['type'] == "application/x-mpegurl":
                            yield from HLSStream.parse_variant_playlist(
                                self.session,
                                source["src"],
                                headers={
                                    "User-Agent": useragents.SAFARI_8,
                                    "Referer": self.url
                                }).items()

            if not found:
                log.error(f"Could not find {video_type} Part {part}")
SWF_URL = "http://www.ardmediathek.de/ard/static/player/base/flash/PluginFlash.swf"
HDCORE_PARAMETER = "?hdcore=3.3.0"
QUALITY_MAP = {
    "auto": "auto",
    3: "544p",
    2: "360p",
    1: "288p",
    0: "144p"
}

_url_re = re.compile(r"http(s)?://(?:(\w+\.)?ardmediathek.de/tv|mediathek.daserste.de/)")
_media_id_re = re.compile(r"/play/(?:media|config)/(\d+)")
_media_schema = validate.Schema({
    "_mediaArray": [{
        "_mediaStreamArray": [{
            validate.optional("_server"): validate.text,
            "_stream": validate.any(validate.text, [validate.text]),
            "_quality": validate.any(int, validate.text)
        }]
    }]
})
_smil_schema = validate.Schema(
    validate.union({
        "base": validate.all(
            validate.xml_find("head/meta"),
            validate.get("base"),
            validate.url(scheme="http")
        ),
        "cdn": validate.all(
            validate.xml_find("head/meta"),
            validate.get("cdn")
Beispiel #40
0
class UStreamTVWsClient(WebsocketClient):
    API_URL = "wss://r{0}-1-{1}-{2}-ws-{3}.ums.ustream.tv:1935/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,
        }
    }
Beispiel #41
0
import re

from streamlink.compat import urlparse
from streamlink.plugin import Plugin
from streamlink.plugin.api import http, validate
from streamlink.stream import RTMPStream, HTTPStream, HLSStream
from streamlink.utils import parse_json, rtmpparse, swfdecompress

_url_re = re.compile("http(s)?://api.dmcloud.net/player/embed/[^/]+/[^/]+")
_rtmp_re = re.compile(b"customURL[^h]+(https://.*?)\\\\")
_info_re = re.compile("var info = (.*);")
_schema = validate.Schema(
    {
        "mode": validate.text,
        validate.optional("mp4_url"): validate.url(scheme="http"),
        validate.optional("ios_url"): validate.url(scheme="http"),
        validate.optional("swf_url"): validate.url(scheme="http"),
    }
)


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

    def _get_rtmp_stream(self, swfurl):
        res = http.get(swfurl)
        swf = swfdecompress(res.content)
        match = _rtmp_re.search(swf)
        if not match:
Beispiel #42
0
_rtmp_re = re.compile(r"""
    (?P<host>rtmp://[^/]+)
    /(?P<app>[^/]+)
    /(?P<playpath>.+)
""", re.VERBOSE)
_url_re = re.compile(r"""
    http(s)?://(\w+\.)?
    dailymotion.com
    (/embed)?/(video|live)
    /(?P<media_id>[^_?/]+)
""", re.VERBOSE)

_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},
from streamlink.stream import AkamaiHDStream, HLSStream

HTTP_HEADERS = {
    "User-Agent": ("Mozilla5.0")
}

_url_re = re.compile("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
})
_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"
                          "[@name='httpBase']"),
        validate.xml_element(attrib={
            "content": validate.text
Beispiel #44
0
_quality_re = re.compile(r"(\d+p)$")
_url_re = re.compile(
    r"""
    http(s)?://(www\.)?(hitbox|smashcast).tv
    /(?P<channel>[^/]+)
    (?:
        (?:/videos)?/(?P<media_id>[^/]+)
    )?
""", re.VERBOSE)

_live_schema = validate.Schema(
    {
        "livestream": [{
            "media_user_name":
            validate.text,
            validate.optional("media_hosted_media"):
            object,
            "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")),
Beispiel #45
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
    }
)

Beispiel #46
0
_access_token_schema = validate.Schema(
    {
        "token": validate.text,
        "sig": validate.text
    }, validate.union((validate.get("sig"), validate.get("token"))))
_token_schema = validate.Schema(
    {
        "chansub": {
            "restricted_bitrates":
            validate.all([validate.text],
                         validate.filter(lambda n: not re.match(
                             r"(.+_)?archives|live|chunked", n)))
        }
    }, 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,
})
Beispiel #47
0
_token_schema = validate.Schema(
    {
        "chansub": {
            "restricted_bitrates": validate.all(
                [validate.text],
                validate.filter(
                    lambda n: not re.match(r"(.+_)?archives|live|chunked", n)
                )
            )
        }
    },
    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,
Beispiel #48
0
class AbemaTV(Plugin):
    '''
    Abema.tv https://abema.tv/
    Note: Streams are geo-restricted to Japan

    '''
    _url_re = re.compile(
        r"""https://abema\.tv/(
        now-on-air/(?P<onair>[^\?]+)
        |
        video/episode/(?P<episode>[^\?]+)
        |
        channels/.+?/slots/(?P<slots>[^\?]+)
        )""", re.VERBOSE)

    _CHANNEL = "https://api.abema.io/v1/channels"

    _USER_API = "https://api.abema.io/v1/users"

    _PRGM_API = "https://api.abema.io/v1/video/programs/{0}"

    _SLOTS_API = "https://api.abema.io/v1/media/slots/{0}"

    _PRGM3U8 = "https://vod-abematv.akamaized.net/program/{0}/playlist.m3u8"

    _SLOTM3U8 = "https://vod-abematv.akamaized.net/slot/{0}/playlist.m3u8"

    SECRETKEY = (b"v+Gjs=25Aw5erR!J8ZuvRrCx*rGswhB&qdHd_SYerEWdU&a?3DzN9B"
                 b"Rbp5KwY4hEmcj5#fykMjJ=AuWz5GSMY-d@H7DMEh3M@9n2G552Us$$"
                 b"k9cD=3TxwWe86!x#Zyhe")

    _USER_SCHEMA = validate.Schema({
        u"profile": {
            u"userId": validate.text
        },
        u"token": validate.text
    })

    _CHANNEL_SCHEMA = validate.Schema({
        u"channels": [{
            u"id": validate.text,
            "name": validate.text,
            "playback": {
                validate.optional(u"dash"): validate.text,
                u"hls": validate.text
            }
        }]
    })

    _PRGM_SCHEMA = validate.Schema(
        {u"label": {
            validate.optional(u"free"): bool
        }})

    _SLOT_SCHEMA = validate.Schema(
        {u"slot": {
            u"flags": {
                validate.optional("timeshiftFree"): bool
            }
        }})

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

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

    def _generate_applicationkeysecret(self, deviceid):
        deviceid = deviceid.encode("utf-8")  # for python3
        # plus 1 hour and drop minute and secs
        # for python3 : floor division
        ts_1hour = (int(time.time()) + 60 * 60) // 3600 * 3600
        time_struct = time.gmtime(ts_1hour)
        ts_1hour_str = str(ts_1hour).encode("utf-8")

        h = hmac.new(self.SECRETKEY, digestmod=hashlib.sha256)
        h.update(self.SECRETKEY)
        tmp = h.digest()
        for i in range(time_struct.tm_mon):
            h = hmac.new(self.SECRETKEY, digestmod=hashlib.sha256)
            h.update(tmp)
            tmp = h.digest()
        h = hmac.new(self.SECRETKEY, digestmod=hashlib.sha256)
        h.update(urlsafe_b64encode(tmp).rstrip(b"=") + deviceid)
        tmp = h.digest()
        for i in range(time_struct.tm_mday % 5):
            h = hmac.new(self.SECRETKEY, digestmod=hashlib.sha256)
            h.update(tmp)
            tmp = h.digest()

        h = hmac.new(self.SECRETKEY, digestmod=hashlib.sha256)
        h.update(urlsafe_b64encode(tmp).rstrip(b"=") + ts_1hour_str)
        tmp = h.digest()

        for i in range(time_struct.tm_hour % 5):  # utc hour
            h = hmac.new(self.SECRETKEY, digestmod=hashlib.sha256)
            h.update(tmp)
            tmp = h.digest()

        return urlsafe_b64encode(tmp).rstrip(b"=").decode("utf-8")

    def _is_playable(self, vtype, vid):
        auth_header = {"Authorization": "Bearer " + self.usertoken}
        if vtype == "episode":
            res = self.session.http.get(self._PRGM_API.format(vid),
                                        headers=auth_header)
            jsonres = self.session.http.json(res, schema=self._PRGM_SCHEMA)
            return jsonres["label"].get("free", False) is True
        elif vtype == "slots":
            res = self.session.http.get(self._SLOTS_API.format(vid),
                                        headers=auth_header)
            jsonres = self.session.http.json(res, schema=self._SLOT_SCHEMA)
            return jsonres["slot"]["flags"].get("timeshiftFree", False) is True

    def _get_streams(self):
        deviceid = str(uuid.uuid4())
        appkeysecret = self._generate_applicationkeysecret(deviceid)
        json_data = {
            "deviceId": deviceid,
            "applicationKeySecret": appkeysecret
        }
        res = self.session.http.post(self._USER_API, json=json_data)
        jsonres = self.session.http.json(res, schema=self._USER_SCHEMA)
        self.usertoken = jsonres['token']  # for authorzation

        matchresult = self._url_re.match(self.url)
        if matchresult.group("onair"):
            onair = matchresult.group("onair")
            if onair == "news-global":
                self._CHANNEL = update_qsd(self._CHANNEL, {"division": "1"})
            res = self.session.http.get(self._CHANNEL)
            jsonres = self.session.http.json(res, schema=self._CHANNEL_SCHEMA)
            channels = jsonres["channels"]
            for channel in channels:
                if onair == channel["id"]:
                    break
            else:
                raise NoStreamsError(self.url)
            playlisturl = channel["playback"]["hls"]
        elif matchresult.group("episode"):
            episode = matchresult.group("episode")
            if not self._is_playable("episode", episode):
                log.error("Premium stream is not playable")
                return {}
            playlisturl = self._PRGM3U8.format(episode)
        elif matchresult.group("slots"):
            slots = matchresult.group("slots")
            if not self._is_playable("slots", slots):
                log.error("Premium stream is not playable")
                return {}
            playlisturl = self._SLOTM3U8.format(slots)

        log.debug("URL={0}".format(playlisturl))

        # hook abematv private protocol
        self.session.http.mount(
            "abematv-license://",
            AbemaTVLicenseAdapter(self.session, deviceid, self.usertoken))

        streams = HLSStream.parse_variant_playlist(self.session, playlisturl)
        if not streams:
            return {"live": HLSStream(self.session, playlisturl)}
        else:
            return streams
Beispiel #49
0
_flashvar_re = re.compile(r"""(['"])(.*?)\1\s*:\s*(['"])(.*?)\3""")
_clientlibs_re = re.compile(r"""<script.*?src=(['"])(.*?/clientlibs_anime_watch.*?\.js)\1""")

_schema = validate.Schema(
    validate.union({
        "flashvars": validate.all(
            validate.transform(_flashvars_re.search),
            validate.get(1),
            validate.transform(_flashvar_re.findall),
            validate.map(lambda v: (v[1], v[3])),
            validate.transform(dict),
            {
                "s": validate.text,
                "country": validate.text,
                "init": validate.text,
                validate.optional("ss_id"): validate.text,
                validate.optional("mv_id"): validate.text,
                validate.optional("device_cd"): validate.text,
                validate.optional("ss1_prm"): validate.text,
                validate.optional("ss2_prm"): validate.text,
                validate.optional("ss3_prm"): validate.text
            }
        ),
        "clientlibs": validate.all(
            validate.transform(_clientlibs_re.search),
            validate.get(2),
            validate.text
        )
    })
)
Beispiel #50
0
def parse_fmt_list(formatsmap):
    formats = {}
    if not formatsmap:
        return formats

    for format in formatsmap.split(","):
        s = format.split("/")
        (w, h) = s[1].split("x")
        formats[int(s[0])] = "{0}p".format(h)

    return formats


_config_schema = validate.Schema(
    {
        validate.optional("fmt_list"): validate.all(
            validate.text,
            validate.transform(parse_fmt_list)
        ),
        validate.optional("url_encoded_fmt_stream_map"): validate.all(
            validate.text,
            validate.transform(parse_stream_map),
            [{
                "itag": validate.all(
                    validate.text,
                    validate.transform(int)
                ),
                "quality": validate.text,
                "url": validate.url(scheme="http"),
                validate.optional("s"): validate.text,
                validate.optional("stereo3d"): validate.all(
_rtmp_re = re.compile("""
    (?P<host>rtmp://[^/]+)
    /(?P<app>[^/]+)
    /(?P<playpath>.+)
""", re.VERBOSE)
_url_re = re.compile("""
    http(s)?://(\w+\.)?
    dailymotion.com
    (/embed)?/(video|live)
    /(?P<media_id>[^_?/]+)
""", re.VERBOSE)

_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},
Beispiel #52
0
_token_schema = validate.Schema(
    {
        "chansub": {
            "restricted_bitrates": validate.all(
                [validate.text],
                validate.filter(
                    lambda n: not re.match(r"(.+_)?archives|live|chunked", n)
                )
            )
        }
    },
    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,
Beispiel #53
0
_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):
        FLVTagConcat.__init__(self, *args, **kwargs)

    def decrypt_data(self, key, iv, data):
Beispiel #54
0
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+)"')
    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:
            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:
                return content_id_m.group("id")

    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")
Beispiel #55
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"
Beispiel #56
0
class NimoTV(Plugin):
    data_url = 'https://m.nimo.tv/{0}'
    data_re = re.compile(r'<script>var G_roomBaseInfo = ({.*?});</script>')

    author = None
    category = None
    title = None

    data_schema = validate.Schema(
        validate.transform(data_re.search),
        validate.any(
            None,
            validate.all(
                validate.get(1),
                validate.transform(parse_json),
                {
                    'title': str,
                    'nickname': str,
                    'game': str,
                    'liveStreamStatus': int,
                    validate.optional('mStreamPkg'): str,
                },
            )),
    )

    video_qualities = {
        250: '240p',
        500: '360p',
        1000: '480p',
        2500: '720p',
        6000: '1080p',
    }

    _re_appid = re.compile(br'appid=(\d+)')
    _re_domain = re.compile(
        br'(https?:\/\/[A-Za-z]{2,3}.hls[A-Za-z\.\/]+)(?:V|&)')
    _re_id = re.compile(br'id=([^|\\]+)')
    _re_tp = re.compile(br'tp=(\d+)')

    def get_author(self):
        return self.author

    def get_category(self):
        return self.category

    def get_title(self):
        return self.title

    def _get_streams(self):
        username = self.match.group('username')
        if not username:
            return

        headers = {'User-Agent': useragents.ANDROID}
        data = self.session.http.get(
            self.data_url.format(username),
            headers=headers,
            schema=self.data_schema,
        )

        if data['liveStreamStatus'] == 0:
            log.info('This stream is currently offline')
            return

        mStreamPkg = data.get('mStreamPkg')
        if not mStreamPkg:
            log.debug('missing mStreamPkg')
            return

        mStreamPkg = bytes.fromhex(mStreamPkg)
        try:
            _appid = self._re_appid.search(mStreamPkg).group(1).decode('utf-8')
            _domain = self._re_domain.search(mStreamPkg).group(1).decode(
                'utf-8')
            _id = self._re_id.search(mStreamPkg).group(1).decode('utf-8')
            _tp = self._re_tp.search(mStreamPkg).group(1).decode('utf-8')
        except AttributeError:
            log.error('invalid mStreamPkg')
            return

        params = {
            'appid': _appid,
            'id': _id,
            'tp': _tp,
            'u': '0',
            't': '100',
            'needwm': 1,
        }
        url = f'{_domain}{_id}.m3u8'
        log.debug(f'URL={url}')
        for k, v in self.video_qualities.items():
            _params = params.copy()
            _params.update({'ratio': k})
            if v == '1080p':
                _params.update({'needwm': 0})
            elif v in ('720p', '480p', '360p'):
                _params.update({'sphd': 1})

            log.trace(f'{v} params={_params!r}')
            # some qualities might not exist, but it will select a different lower quality
            yield v, HLSStream(self.session, url, params=_params)

        self.author = data['nickname']
        self.category = data['game']
        self.title = data['title']
Beispiel #57
0
QUALITYS = ["original", "hd", "sd"]

QUALITY_WEIGHTS = {
    "original": 1080,
    "hd": 720,
    "sd": 480
}

_url_re = re.compile(r"http(s)?://(?P<cdn>\w+\.)?afreeca(tv)?\.com/(?P<username>\w+)(/\d+)?")

_channel_schema = validate.Schema(
    {
        "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
Beispiel #58
0
class TVPlayer(Plugin):
    context_url = "http://tvplayer.com/watch/context"
    api_url = "http://api.tvplayer.com/api/v2/stream/live"
    login_url = "https://tvplayer.com/account/login"
    update_url = "https://tvplayer.com/account/update-detail"
    dummy_postcode = "SE1 9LT"  # location of ITV HQ in London

    url_re = re.compile(
        r"https?://(?:www.)?tvplayer.com/(:?watch/?|watch/(.+)?)")
    stream_attrs_re = re.compile(
        r'data-(resource|token|channel-id)\s*=\s*"(.*?)"', re.S)
    data_id_re = re.compile(r'data-id\s*=\s*"(.*?)"', re.S)
    login_token_re = re.compile(r'input.*?name="token".*?value="(\w+)"')
    stream_schema = validate.Schema(
        {
            "tvplayer":
            validate.Schema({
                "status":
                u'200 OK',
                "response":
                validate.Schema({
                    "stream":
                    validate.url(scheme=validate.any("http", "https")),
                    validate.optional("drmToken"):
                    validate.any(None, validate.text)
                })
            })
        }, validate.get("tvplayer"), validate.get("response"))
    context_schema = validate.Schema({
        "validate": validate.text,
        validate.optional("token"): validate.text,
        "platform": {
            "key": validate.text
        }
    })
    arguments = PluginArguments(
        PluginArgument(
            "email",
            help="The email address used to register with tvplayer.com.",
            metavar="EMAIL",
            requires=["password"]),
        PluginArgument("password",
                       sensitive=True,
                       help="The password for your tvplayer.com account.",
                       metavar="PASSWORD"))

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

    def __init__(self, url):
        super(TVPlayer, self).__init__(url)
        http.headers.update({"User-Agent": useragents.CHROME})

    def authenticate(self, username, password):
        res = http.get(self.login_url)
        match = self.login_token_re.search(res.text)
        token = match and match.group(1)
        res2 = http.post(self.login_url,
                         data=dict(email=username,
                                   password=password,
                                   token=token),
                         allow_redirects=False)
        # there is a 302 redirect on a successful login
        return res2.status_code == 302

    def _get_stream_data(self, resource, channel_id, token, service=1):
        # Get the context info (validation token and platform)
        self.logger.debug(
            "Getting stream information for resource={0}".format(resource))
        context_res = http.get(self.context_url,
                               params={
                                   "resource": resource,
                                   "gen": token
                               })
        context_data = http.json(context_res, schema=self.context_schema)
        self.logger.debug("Context data: {0}", str(context_data))

        # get the stream urls
        res = http.post(self.api_url,
                        data=dict(service=service,
                                  id=channel_id,
                                  validate=context_data["validate"],
                                  token=context_data.get("token"),
                                  platform=context_data["platform"]["key"]),
                        raise_for_status=False)

        return http.json(res, schema=self.stream_schema)

    def _get_stream_attrs(self, page):
        stream_attrs = dict(
            (k.replace("-", "_"), v.strip('"'))
            for k, v in self.stream_attrs_re.findall(page.text))

        if not stream_attrs.get("channel_id"):
            m = self.data_id_re.search(page.text)
            stream_attrs["channel_id"] = m and m.group(1)

        self.logger.debug("Got stream attributes: {0}", str(stream_attrs))
        valid = True
        for a in ("channel_id", "resource", "token"):
            if a not in stream_attrs:
                self.logger.debug("Missing '{0}' from stream attributes", a)
                valid = False

        return stream_attrs if valid else {}

    def _get_streams(self):
        if self.get_option("email") and self.get_option("password"):
            self.logger.debug("Logging in as {0}".format(
                self.get_option("email")))
            if not self.authenticate(self.get_option("email"),
                                     self.get_option("password")):
                self.logger.warning("Failed to login as {0}".format(
                    self.get_option("email")))

        # find the list of channels from the html in the page
        self.url = self.url.replace("https", "http")  # https redirects to http
        res = http.get(self.url)

        if "enter your postcode" in res.text:
            self.logger.info(
                "Setting your postcode to: {0}. "
                "This can be changed in the settings on tvplayer.com",
                self.dummy_postcode)
            res = http.post(self.update_url,
                            data=dict(postcode=self.dummy_postcode),
                            params=dict(return_url=self.url))

        stream_attrs = self._get_stream_attrs(res)
        if stream_attrs:
            stream_data = self._get_stream_data(**stream_attrs)

            if stream_data:
                if stream_data.get("drmToken"):
                    self.logger.error(
                        "This stream is protected by DRM can cannot be played")
                    return
                else:
                    return HLSStream.parse_variant_playlist(
                        self.session, stream_data["stream"])
        else:
            if "need to login" in res.text:
                self.logger.error(
                    "You need to login using --tvplayer-email/--tvplayer-password to view this stream"
                )
    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,
                }],
            ),
            validate.optional("url"): validate.text,
        }]
    )
})
_stream_schema = validate.Schema(
Beispiel #60
0
SWF_URL = "http://mips.tv/content/scripts/eplayer.swf"

_url_re = re.compile(r"http(s)?://(\w+.)?mips.tv/(?P<channel>[^/&?]+)")
_flashvars_re = re.compile(r"'FlashVars', '([^']+)'")
_rtmp_re = re.compile(r"redirect=(.+)")

_schema = validate.Schema(
    validate.transform(_flashvars_re.search),
    validate.any(
        None,
        validate.all(
            validate.get(1),
            validate.transform(parse_query),
            {
                "id": validate.transform(int),
                validate.optional("s"): validate.text
            }
        )
    )
)
_rtmp_schema = validate.Schema(
    validate.transform(_rtmp_re.search),
    validate.get(1),
)


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