Пример #1
0
    def __post_bind(self, sc, postdata):  # type: (str, dict) -> None
        self.ofs += 1
        post_data = {"count": "1", "ofs": str(self.ofs), "req0__sc": sc}
        for key in list(postdata.keys()):
            post_data["req0_" + key] = postdata[key]

        if get_setting_as_bool("debug-http"):
            logger.debug("POST %s:\n%r", sc, post_data)

        bind_vals = self.bind_vals
        bind_vals["RID"] = "1337"
        url = "{}/api/lounge/bc/bind?{}".format(self.base_url, urlencode(bind_vals))
        verify_ssl = get_setting_as_bool("verify-ssl")

        last_exc = None
        for i in range(MAX_SEND_RETRIES):
            try:
                self.session.post(url, data=post_data, verify=verify_ssl)
            except requests.ConnectionError as e:
                logger.info("failed to send data on attempt %s/%s", i + 1, MAX_SEND_RETRIES)
                last_exc = e
                continue
            except Exception:
                logger.exception("error sending %s", sc)
                break
            else:
                # request successful
                break
        else:
            # MAX_SEND_RETRIES exceeded
            logger.exception("failed to send data to client", exc_info=last_exc)
Пример #2
0
 def datagram_received(self, datagram, address):
     if get_setting_as_bool('debug-ssdp'):
         logger.debug('Datagram received. Address:{}; Content:{}'.format(
             address, datagram))
     if "urn:dial-multiscreen-org:service:dial:1" in datagram and "M-SEARCH" in datagram:
         if get_setting_as_bool('debug-ssdp'):
             logger.debug("Answering datagram")
         data = build_template(self.header).render(
             ip=self.get_remote_ip(address), uuid=Kodicast.uuid)
         self.reply(data, address)
Пример #3
0
 def _generate_screen_id(self):
     screen_id = self.session.get(
         "{}/api/lounge/pairing/generate_screen_id".format(self.base_url),
         verify=get_setting_as_bool("verify-ssl"))
     self.screen_id = screen_id.text
     logger.debug("Screen ID is: {}".format(self.screen_id))
     return self.screen_id
Пример #4
0
 def _get_lounge_token_batch(self):
     token_info = self.session.post(
         "{}/api/lounge/pairing/get_lounge_token_batch".format(self.base_url),
         data={"screen_ids": self.screen_id},
         verify=get_setting_as_bool("verify-ssl")
     ).json()
     self.lounge_token = token_info["screens"][0]["loungeToken"]
     logger.debug("Lounge Token: {}".format(self.lounge_token))
     self.bind_vals["loungeIdToken"] = self.lounge_token
     return self.lounge_token
Пример #5
0
def run():
    generate_uuid()

    # Start HTTP server
    chromecast = Chromecast(monitor)
    chromecast_addr = chromecast.start()

    # Start SSDP service
    if get_setting_as_bool('enable-ssdp'):
        ssdp_server = SSDPserver()
        ssdp_server.start(chromecast_addr, interfaces=Kodicast.interfaces)

    while not monitor.abortRequested():
        monitor.waitForAbort(1)

    # Abort services
    if get_setting_as_bool('enable-ssdp'):
        ssdp_server.shutdown()
    chromecast.abort()
Пример #6
0
 def _bind(self):
     self.ofs += 1
     bind_vals = self.bind_vals
     bind_vals["CVER"] = "1"
     bind_info = self.session.post(
         "{}/api/lounge/bc/bind?{}".format(self.base_url, urlencode(bind_vals)),
         data={"count": "0"},
         verify=get_setting_as_bool("verify-ssl")
     ).text
     for line in bind_info.split("\n"):
         self.handle_cmd(str(line))
Пример #7
0
 def _bind(self):
     self.ofs += 1
     bind_vals = self.bind_vals
     bind_vals["CVER"] = "1"
     bind_info = self.session.post(
         "{}/api/lounge/bc/bind?{}".format(self.base_url, urlencode(bind_vals)),
         data={"count": "0"},
         verify=get_setting_as_bool("verify-ssl")
     ).text
     for cmd in CommandParser(bind_info):
         self.handle_cmd(cmd)
Пример #8
0
 def _get_pairing_code(self):
     r = self.session.post(
         "{}/api/lounge/pairing/get_pairing_code?ctx=pair".format(self.base_url),
         data={
             "access_type": "permanent",
             "app": self.default_screen_app,
             "lounge_token": self.lounge_token,
             "screen_id": self.screen_id,
             "screen_name": self.default_screen_name
         },
         verify=get_setting_as_bool("verify-ssl")
     )
     return "{}-{}-{}-{}".format(r.text[0:3], r.text[3:6], r.text[6:9], r.text[9:12])
Пример #9
0
 def _register_pairing_code(self):
     r = self.session.post(
         "{}/api/lounge/pairing/register_pairing_code".format(self.base_url),
         data={
             "access_type": "permanent",
             "app": self.default_screen_app,
             "pairing_code": self.pairing_code,
             "screen_id": self.screen_id,
             "screen_name": self.default_screen_name
             },
         verify=get_setting_as_bool("verify-ssl")
     )
     logger.debug("Registered pairing code status code: {}".format(r.status_code))
Пример #10
0
    def __post_bind(self, sc, postdata):
        self.ofs += 1
        post_data = {"count": "1", "ofs": str(self.ofs)}
        post_data["req0__sc"] = sc
        for key in list(postdata.keys()):
            post_data["req0_" + key] = postdata[key]

        bind_vals = self.bind_vals
        bind_vals["RID"] = "1337"
        self.session.post("{}/api/lounge/bc/bind?{}".format(
            self.base_url, urllib.urlencode(bind_vals)),
                          data=post_data,
                          verify=get_setting_as_bool("verify-ssl"))
Пример #11
0
    def _listen(self):
        logger.debug("Listening to youtube remote events...")
        self.app.ofs += 1
        bind_vals = self.app.bind_vals.copy()
        bind_vals["RID"] = "rpc"
        bind_vals["CI"] = "0"
        bind_vals["TYPE"] = "xmlhttp"
        bind_vals["AID"] = "3"
        url = "{}/api/lounge/bc/bind?{}".format(self.app.base_url, urlencode(bind_vals))

        debug_http = get_setting_as_bool("debug-http")

        parser = CommandParser()
        for chunk in self.__read_cmd_chunks(url):
            if debug_http:
                logger.debug("received chunk %r", chunk)

            parser.write(chunk.decode("utf-8"))
            for cmd in parser.get_commands():
                self.app.handle_cmd(cmd)
Пример #12
0
    def _parse_pending(self):
        if not self.pending:
            return

        debug_cmds = logger.isEnabledFor(
            logging.DEBUG) and get_setting_as_bool("debug-cmd")

        end_index = 0
        try:
            for match in CMD_PATTERN.finditer(self.pending):
                if debug_cmds:
                    skipped = self.pending[end_index:match.start()]
                    if skipped:
                        logger.debug("command parser skipped: %r", skipped)

                end_index = match.end()
                try:
                    cmd = _command_from_match(match)
                except SyntaxError:
                    logger.exception("unable to parse command")
                else:
                    yield cmd
        finally:
            self.pending = self.pending[end_index:]
Пример #13
0
 def _get_list_info(self, list_id):
     r = self.session.get(
         "{}/list_ajax?style=json&action_get_list=1&list={}".format(self.base_url, list_id),
         verify=get_setting_as_bool("verify-ssl")
     )
     return r.json()["video"]
Пример #14
0
    def handle_cmd(self, cmd):
        if get_setting_as_bool('debug-cmd'):
            logger.debug("CMD: {}".format(cmd))

        if case("c", cmd):
            logger.debug("C cmd received")
            self.sid = re.findall('"c","(.+?)"', cmd)[0]
            self.bind_vals["SID"] = self.sid

        elif case("S", cmd):
            logger.debug("Session established received")
            self.session_id = re.findall('"S","(.+?)"', cmd)[0]
            self.bind_vals["gsessionid"] = self.session_id

        elif case("remoteConnected", cmd):
            # Parse data
            code, data = parse_cmd(cmd)
            if code > self.code:
                self.code = code
                logger.info("Remote connected: {}".format(data))
                self.has_client = True
                if not self.player:
                    # Start "player" thread
                    threading.Thread(target=self.__player_thread).start()
                # Start a new volume_monitor if not yet available
                if not self.volume_monitor:
                    threading.Thread(target=self.__monitor_volume).start()
                # Disable automatic playback from youtube (this is kodi not youtube :))
                self._set_disabled()
                # Check if it is a new association
                if not self.connected_client or self.connected_client != data:
                    self.connected_client = data
                    kodibrigde.remote_connected(data["name"])
            else:
                logger.debug("Command ignored, already executed before")

        elif case("remoteDisconnected", cmd):
            code, data = parse_cmd(cmd)
            if code > self.code:
                self.code = code
                logger.info("Remote disconnected: {}".format(data))
                self._initial_app_state()
                kodibrigde.remote_disconnected(data["name"])
                # Kill player if exists
                if self.player and self.player.isPlaying:
                    self._ready()
            else:
                logger.debug("Command ignored, already executed before")

        elif case("getNowPlaying", cmd):
            logger.debug("getNowPlaying received")
            self._ready()

        elif case("setPlaylist", cmd):
            code, data = parse_cmd(cmd)
            if code > self.code:
                self.code = code
                logger.debug("setPlaylist: {}".format(data))
                cur_video_id = data["videoId"]
                video_ids = data["videoIds"]
                if 'ctt' in data:
                    self.ctt = data["ctt"]
                self.cur_list_id = data["listId"]
                self.current_index = int(data["currentIndex"])
                self.cur_list = video_ids.split(",")
                # Set info on our custom player instance and request playback
                self.player.setInfo(cur_video_id, self.ctt, self.cur_list_id, self.current_index)
                self.player.play_from_youtube(kodibrigde.get_youtube_plugin_path(cur_video_id))
            else:
                logger.debug("Command ignored, already executed before")

        elif case("updatePlaylist", cmd):
            code, data = parse_cmd(cmd)
            if code > self.code:
                self.code = code
                logger.debug("updatePlaylist: {}".format(data))
                if "videoIds" in list(data.keys()):
                    self.cur_list = data["videoIds"].split(",")
                    if self.current_index and self.current_index >= len(self.cur_list):
                        self.current_index -= 1
                else:
                    self.cur_list = []
                    self.current_index = 0
                    # Check if kodi is playing and if so request stop
                    if self.player.playing:
                        self.player.stop()
            else:
                logger.debug("Command ignored, already executed before")

        elif case("next", cmd):
            logger.debug("Next received")
            if self.current_index + 1 < len(self.cur_list):
                self._next()

        elif case("previous", cmd):
            logger.debug("Previous received")
            if self.current_index > 0:
                self._previous()

        elif case("pause", cmd):
            logger.debug("Pause received")
            self._pause()

        elif case("stopVideo", cmd):
            logger.debug("stopVideo received")
            if self.player and self.player.playing:
                self._ready()

        elif case("seekTo", cmd):
            code, data = parse_cmd(cmd)
            if code > self.code:
                self.code = code
                logger.debug("seekTo: {}".format(data))
                time_seek = data["newTime"]
                self._seek(time_seek)
            else:
                logger.debug("Command ignored, already executed before")

        elif case("getVolume", cmd):
            logger.debug("getVolume received")
            self._get_volume()

        elif case("setVolume", cmd):
            code, data = parse_cmd(cmd)
            if code > self.code:
                self.code = code
                logger.debug("setVolume: {}".format(data))
                new_volume = data["volume"]
                # Set volume only if it differs from current volume
                if new_volume != kodibrigde.get_kodi_volume():
                    self._set_volume(new_volume)
            else:
                logger.debug("Command ignored, already executed before")

        elif case("play", cmd):
            logger.debug("play received")
            self.play_state = 1
            self._resume()
Пример #15
0
    def handle_cmd(self, cmd):  # type: (Command) -> None
        debug_cmds = get_setting_as_bool('debug-cmd')

        if debug_cmds:
            logger.debug("CMD: %s", cmd)

        code, name, data = cmd

        if code <= self.code:
            if debug_cmds:
                logger.debug("Command ignored, already executed before")
            return

        self.code = code

        if name == "c":
            logger.debug("C cmd received")
            self.bind_vals["SID"] = data[0]

        elif name == "S":
            logger.debug("Session established received")
            self.bind_vals["gsessionid"] = data

        elif name == "remoteConnected":
            logger.info("Remote connected: {}".format(data))
            if not self.player:
                # Start "player" thread
                threading.Thread(name="Player",
                                 target=self.__player_thread).start()
            # Start a new volume_monitor if not yet available
            if not self.volume_monitor:
                self.volume_monitor = VolumeMonitor(self)
                self.volume_monitor.start()

            # Disable automatic playback from youtube (this is kodi not youtube :))
            # TODO: see issue #15
            self._disable_autoplay()
            # Check if it is a new association
            if self.connected_client != data:
                self.connected_client = data
                kodibrigde.remote_connected(data["name"])

        elif name == "remoteDisconnected":
            logger.info("Remote disconnected: {}".format(data))
            self._initial_app_state()
            kodibrigde.remote_disconnected(data["name"])

        elif name == "getNowPlaying":
            logger.debug("getNowPlaying received")
            self.report_now_playing()

        elif name == "setPlaylist":
            logger.debug("setPlaylist: {}".format(data))
            self.state.handle_set_playlist(data)
            play_url = kodibrigde.get_youtube_plugin_path(self.state.video_id, seek=data.get("currentTime", 0))
            self.player.play_from_youtube(play_url)

        elif name == "updatePlaylist":
            logger.debug("updatePlaylist: {}".format(data))
            self.state.handle_update_playlist(data)
            if not self.state.has_playlist and self.player.isPlaying():
                self.player.stop()

        elif name == "next":
            logger.debug("Next received")
            self._next()

        elif name == "previous":
            logger.debug("Previous received")
            self._previous()

        elif name == "pause":
            logger.debug("Pause received")
            self._pause()

        elif name == "stopVideo":
            logger.debug("stopVideo received")
            if self.player.isPlaying():
                self.player.stop()

        elif name == "seekTo":
            logger.debug("seekTo: {}".format(data))
            self._seek(int(data["newTime"]))

        elif name == "getVolume":
            logger.debug("getVolume received")
            volume = kodibrigde.get_kodi_volume()
            self.report_volume(volume)

        elif name == "setVolume":
            logger.debug("setVolume: {}".format(data))
            new_volume = data["volume"]
            # Set volume only if it differs from current volume
            if new_volume != kodibrigde.get_kodi_volume():
                self._set_volume(new_volume)

        elif name == "play":
            logger.debug("play received")
            self._resume()

        elif debug_cmds:
            logger.debug("unhandled command: %r", name)