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)
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)
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
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
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()
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))
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)
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])
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))
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"))
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)
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:]
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"]
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()
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)