def choose_plugin(self, key=None, identifier=None, default_identifier=None): plugins = [] try: plugins = self.plugins = self.server_plugins.get("Directory", []) except HTTPError as e: if e.response.status_code == 401: print( "Access denied when accessing plugins on {}, going to server selection" .format(mask_str(self.server_name))) message( "Access denied for plugins on {}".format(self.server_name), "ERROR") raise cherrypy.HTTPRedirect(cherrypy.url("/servers")) if (key and identifier) or default_identifier: ident = identifier or default_identifier for plugin in plugins: if plugin["identifier"] == ident: self.plugin = plugin print("Plugin chosen: {}".format(plugin["title"])) raise cherrypy.HTTPRedirect(cherrypy.url("/")) template = env.get_template('plugins.jinja2') return template.render(plex_headers_json=json.dumps(self.plex_headers), **self.default_context)
def get_plex_token(self, username=None, password=None, token=None): if username and password: r = self.session.post("https://plex.tv/users/sign_in.json", { "user[login]": username, "user[password]": password, }, headers=self.plex_headers, **self.req_defaults) try: r.raise_for_status() except HTTPError as e: if e.response.status_code == 401: message("Wrong username and/or password", "ERROR") else: self.plex_token = r.json()["user"]["authToken"] print("Token received via credentials login") raise cherrypy.HTTPRedirect(cherrypy.url("/")) if token: self.plex_token = token print("Token received via plex.tv auth") return json.dumps({"url": cherrypy.url(self.prefix)}) template = env.get_template('token.jinja2') return template.render(plex_headers_json=json.dumps(self.plex_headers), **self.default_context)
def default(self, *args, **kwargs): self.ensure_pms_data() query_params = "&".join("=".join([k, v]) for k, v in kwargs.items()) path = "/".join(args) + ("?" + query_params if query_params else "") # print(args, path) if not path: path = self.plugin["key"][1:] try: return self.render_plugin(path) except (HTTPError, Timeout) as e: if isinstance(e, HTTPError): if e.response.status_code == 401: message("Access denied on {}".format(self.server_name), "ERROR") print("Access denied when accessing {}," " going to server selection".format( mask_str(self.server_name))) self.server_name = None self.connection = None raise cherrypy.HTTPRedirect(cherrypy.url("/servers")) elif e.response.status_code == 404: raise cherrypy.HTTPRedirect(cherrypy.url("/plugins")) else: message("Timeout on {}".format(self.server_name), "WARNING") print( "Error when connecting to '{}', trying other connection to: {}" .format(mask_url(self.server_addr), mask_str(self.server_name))) return self.discover_pms(self.server_name) except: print("Something went wrong. {}".format(traceback.format_exc()))
def plugin_restart(self, *args, **kwargs): self.ensure_pms_data() path = ":/plugins/{}/restart".format(self.plugin["identifier"]) r = self._dispatch(path) if r.status_code == 200: message("Plugin restart triggered", "SUCCESS") else: message("Something went wrong when trying to restart the plugin", "ERROR") raise cherrypy.HTTPRedirect(cherrypy.url("/"))
def render_plugin(self, path, template=template, merge_item_keys=("Video", "Directory"), only_return_items=False): content = self.plex_dispatch(path) try: has_content = int(content["size"]) > 0 except ValueError: has_content = False if not has_content: redirect = content.get("title2", None) # this is basically meant for SZ. title2 can contain a full URL to which we will redirect if redirect and self.is_url(redirect): if self.connections: f = furl(redirect) # try finding the current PMS in the link is_current_pms = filter( lambda c: c["address"] == f.host or f.host in c["url"], self.connections) if is_current_pms: # use current PMS connection for the link con = furl(self.server_addr) f.host = con.host f.port = con.port redirect = f r = requests.get(f) # special handling for data if r.headers['content-type'] != 'text/html': data = io.BytesIO(r.content) # set headers for hdr in ("Content-Type", "Content-Disposition", "Content-Length"): cherrypy.response.headers[hdr] = r.headers[hdr] # serve return static.serve_fileobj(data) raise cherrypy.HTTPRedirect(redirect) message("No plugin data returned", "WARNING") print("No plugin data returned, returning to plugin selection") self.plugin = None raise cherrypy.HTTPRedirect(cherrypy.url("/")) items = self.merge_plugin_data(content, keys=merge_item_keys) if only_return_items: return items return template.render(data=content, items=items, **self.default_context)
def maintenance(): if kitana.has_update: message("Version {} is available. Please update{}".format(kitana.has_update, " your docker container" if kitana.running_as == "docker" else ""), persistent=True, data={"version": kitana.VERSION, "new_version": kitana.has_update}) kitana.has_update = False if not kitana.maintenance_ran: try: kitana.run_maintenance() except: pass kitana.maintenance_ran = True
def render_plugin(self, path): content = self.plex_dispatch(path) try: has_content = int(content["size"]) > 0 except ValueError: has_content = False if not has_content: message("No plugin data returned", "WARNING") print("No plugin data returned, returning to plugin selection") self.plugin = None raise cherrypy.HTTPRedirect(cherrypy.url("/")) items = self.merge_plugin_data(content) content["Directory"] = None content["Video"] = None return template.render(data=content, items=items, **self.default_context)
def plugin_prefs(self, *args, **kwargs): self.ensure_pms_data() url = ":/plugins/{}/prefs".format(self.plugin["identifier"]) if cherrypy.request.method == "POST": items = self.render_plugin(url, settings_template, merge_item_keys=("Setting", ), only_return_items=True) diff = {} for item in items: new_val = Kitana.normalize_post_ret_value( kwargs.get(item["id"])) if item["value"] != new_val: diff[item["id"]] = new_val if diff: path = "{}/:/prefs/set".format(self.plugin["key"][1:]) r = self._dispatch(path, data=diff) if r.status_code == 200: message("Settings saved", "SUCCESS") else: message("Something went wrong", "ERROR") raise cherrypy.HTTPRedirect(cherrypy.url("/")) else: print("No changed settings") message("No settings have been changed", "SECONDARY") raise cherrypy.HTTPRedirect(cherrypy.url("/")) else: return self.render_plugin(url, settings_template, merge_item_keys=("Setting", ))
def discover_pms(self, server_name=None, server_addr=None, blacklist_addr=None): try: r = self.session.get( "https://plex.tv/api/resources?includeHttps=1&includeRelay=1", headers=self.full_headers, timeout=self.plextv_timeout) r.raise_for_status() except (HTTPError, Timeout) as e: if isinstance(e, HTTPError): if e.response.status_code == 401: self.plex_token = None self.server_name = None self.connection = None print("Access denied when accessing {}, going to login". format(self.server_name)) raise cherrypy.HTTPRedirect(cherrypy.url("/token")) raise content = xmltodict.parse(r.content, attr_prefix="", force_list=("Device", "Connection")) servers = OrderedDict() # import pprint # pprint.pprint(content) use_connection = None connections = [] for device in content["MediaContainer"].get("Device", []): if device.get("provides") != "server" or not bool( device.get("presence")): continue public_address_matches = device.get("publicAddressMatches", "0") == "1" https_required = device.get("httpsRequired", "0") == "1" for connection in device.get("Connection", []): connection["unavailable"] = False if not public_address_matches and connection.get("local", "0") == "1": continue elif https_required and connection.get("protocol", "http") != "https": continue if device["name"] not in servers: if self.only_owned and not device.get("owned", "0") == "1": continue servers[device["name"]] = { "connections": [], "owned": device.get("owned", "0") == "1", "publicAddress": device.get("publicAddress", None), "publicAddressMatches": public_address_matches } if blacklist_addr and connection["uri"] in blacklist_addr: print("{}: {} on blacklist, skipping".format( mask_str(device["name"]), mask_url(connection["uri"]))) connection["unavailable"] = True continue servers[device["name"]]["connections"].append(connection) if server_name and server_name == device["name"]: if server_addr and connection["uri"] == server_addr: use_connection = connection elif server_addr and server_addr == "relay" and connection.get( "relay") == "1": use_connection = connection elif not server_addr: use_connection = connection if use_connection: connections = device["Connection"] break if server_name and use_connection: self.server_name = server_name self.connection = use_connection self.connections = connections server_addr = use_connection["uri"] print("Server set to: {}, {}".format(mask_str(server_name), mask_url(server_addr))) print("Verifying {}: {}".format(mask_str(server_name), mask_url(server_addr))) try: self.session.get(self.server_addr + "servers", headers=self.full_headers, **self.req_defaults) except HTTPError as e: if e.response.status_code == 401: self.plex_token = None self.server_name = None self.connection = None print("Access denied when accessing {}, going to login". format(mask_str(self.server_name))) raise cherrypy.HTTPRedirect(cherrypy.url("/token")) except Timeout as e: if not blacklist_addr: blacklist_addr = [] blacklist_addr.append(server_addr) print("{}: Blacklisting {} due to: {!r}".format( mask_str(server_name), mask_url(server_addr), type(e))) return self.discover_pms(server_name=server_name, server_addr=None, blacklist_addr=blacklist_addr) print("Verified {}: {}".format(mask_str(server_name), mask_url(server_addr))) self.plugin = None message("Successfully connected to {}".format(self.server_name), "SUCCESS") raise cherrypy.HTTPRedirect(cherrypy.url("/")) return servers
def default(self, *args, **kwargs): self.ensure_pms_data() query_params = "&".join("=".join([k, v]) for k, v in kwargs.items()) path = "/".join(args) + ("?" + query_params if query_params else "") # print(args, path) if not path: path = self.plugin["key"][1:] try: return self.render_plugin(path) except (HTTPError, Timeout, requests.exceptions.SSLError) as e: urlTried = getattr(getattr(e, "request", ""), "url", None) if urlTried: urlTried = mask_url(urlTried) try: if not isinstance(e, Timeout): if hasattr(e.response, "status_code"): if e.response.status_code == 401: message( "Access denied on {}".format(self.server_name), "ERROR") print("Access denied when accessing {}," " going to server selection".format( mask_str(self.server_name))) self.server_name = None self.connection = None raise cherrypy.HTTPRedirect( cherrypy.url("/servers")) elif e.response.status_code == 404: raise cherrypy.HTTPRedirect(cherrypy.url("/")) else: message("Timeout on {}".format(self.server_name), "WARNING") if hasattr(e.response, "text") and e.response.text: print("Plugin response: {}".format(e.response.text)) if hasattr(e.response, "status_code") and e.response.status_code != 500: message( "Error on {} (see log): {}".format( self.server_name, e), "ERROR") traceback.print_exc() print( "Error when connecting to '{}', trying other connection to: {}" .format(mask_url(self.server_addr), mask_str(self.server_name))) return self.discover_pms(self.server_name) else: message( "Plugin error on {} (see log): {}".format( self.server_name, e), "ERROR") traceback.print_exc() raise cherrypy.HTTPRedirect(cherrypy.url("/")) except cherrypy.HTTPRedirect: # intercept redirects to avoid infinite redirects lastUrls = cherrypy.session.get("last_urls", []) lastUrls.append(urlTried) if lastUrls.count(urlTried) > 2: print("Tried to reach {} 3 times. Not retrying.".format( urlTried)) message( "Tried to reach {} 3 times. Not retrying.".format( urlTried), "ERROR") cherrypy.session["last_urls"] = [] return self.discover_pms(self.server_name) cherrypy.session["last_urls"] = lastUrls raise except HTTPRedirect: raise except: print("Something went wrong. {}".format(traceback.format_exc()))