def _api_validate(self, *args, **kwargs): """ Sets class vars and remove unneeded parameters. """ if not plexpy.CONFIG.API_ENABLED: self._api_msg = 'API not enabled' elif not plexpy.CONFIG.API_KEY: self._api_msg = 'API key not generated' elif len(plexpy.CONFIG.API_KEY) != 32: self._api_msg = 'API key not generated correctly' elif 'apikey' not in kwargs: self._api_msg = 'Parameter apikey is required' elif 'cmd' not in kwargs: self._api_msg = 'Parameter cmd is required. Possible commands are: %s' % ', '.join(self._api_valid_methods) elif 'cmd' in kwargs and kwargs.get('cmd') not in self._api_valid_methods: self._api_msg = 'Unknown command: %s. Possible commands are: %s' % (kwargs.get('cmd', ''), ', '.join(sorted(self._api_valid_methods))) self._api_callback = kwargs.pop('callback', None) self._api_apikey = kwargs.pop('apikey', None) self._api_cmd = kwargs.pop('cmd', None) self._api_debug = kwargs.pop('debug', False) self._api_profileme = kwargs.pop('profileme', None) # Allow override for the api. self._api_out_type = kwargs.pop('out_type', 'json') if 'app' in kwargs and kwargs.pop('app') == 'true': self._api_app = True if plexpy.CONFIG.API_ENABLED and not self._api_msg: if self._api_apikey == plexpy.CONFIG.API_KEY or (self._api_app and self._api_apikey == mobile_app.TEMP_DEVICE_TOKEN): self._api_authenticated = True elif self._api_app and mobile_app.get_mobile_device_by_token(self._api_apikey): mobile_app.set_last_seen(self._api_apikey) self._api_authenticated = True else: self._api_msg = 'Invalid apikey' if self._api_authenticated and self._api_cmd in self._api_valid_methods: self._api_msg = None self._api_kwargs = kwargs elif not self._api_authenticated and self._api_cmd in ('get_apikey', 'docs', 'docs_md'): self._api_authenticated = True # Remove the old error msg self._api_msg = None self._api_kwargs = kwargs if self._api_msg: logger.api_debug(u'Tautulli APIv2 :: %s.' % self._api_msg) logger.api_debug(u'Tautulli APIv2 :: Cleaned kwargs: %s' % self._api_kwargs) return self._api_kwargs
def notify_newsletter(self, newsletter_id='', subject='', body='', message='', **kwargs): """ Send a newsletter using Tautulli. ``` Required parameters: newsletter_id (int): The ID number of the newsletter agent Optional parameters: subject (str): The subject of the newsletter body (str): The body of the newsletter message (str): The message of the newsletter Returns: None ``` """ if not newsletter_id: self._api_msg = 'Newsletter failed: no newsletter id provided.' self._api_result_type = 'error' return newsletter = newsletters.get_newsletter_config( newsletter_id=newsletter_id) if not newsletter: self._api_msg = 'Newsletter failed: invalid newsletter_id provided %s.' % newsletter_id self._api_result_type = 'error' return logger.api_debug('Tautulli APIv2 :: Sending newsletter.') success = newsletter_handler.notify(newsletter_id=newsletter_id, notify_action='api', subject=subject, body=body, message=message, **kwargs) if success: self._api_msg = 'Newsletter sent.' self._api_result_type = 'success' else: self._api_msg = 'Newsletter failed.' self._api_result_type = 'error' return
def notify(self, notifier_id='', subject='Tautulli', body='Test notification', **kwargs): """ Send a notification using Tautulli. ``` Required parameters: notifier_id (int): The ID number of the notification agent subject (str): The subject of the message body (str): The body of the message Optional parameters: headers (str): The JSON headers for webhook notifications script_args (str): The arguments for script notifications Returns: None ``` """ if not notifier_id: self._api_msg = 'Notification failed: no notifier id provided.' self._api_result_type = 'error' return notifier = notifiers.get_notifier_config(notifier_id=notifier_id) if not notifier: self._api_msg = 'Notification failed: invalid notifier_id provided %s.' % notifier_id self._api_result_type = 'error' return logger.api_debug(u'Tautulli APIv2 :: Sending notification.') success = notification_handler.notify(notifier_id=notifier_id, notify_action='api', subject=subject, body=body, **kwargs) if success: self._api_msg = 'Notification sent.' self._api_result_type = 'success' else: self._api_msg = 'Notification failed.' self._api_result_type = 'error' return
def _api_run(self, *args, **kwargs): """ handles the stuff from the handler """ # Make sure the device ID is not shown in the logs if self._api_cmd == 'register_device': if kwargs.get('device_id'): logger._BLACKLIST_WORDS.add(kwargs['device_id']) if kwargs.get('onesignal_id'): logger._BLACKLIST_WORDS.add(kwargs['onesignal_id']) result = {} logger.api_debug('Tautulli APIv2 :: API called with kwargs: %s' % kwargs) self._api_validate(**kwargs) if self._api_cmd and self._api_authenticated: call = getattr(self, self._api_cmd) # Profile is written to console. if self._api_profileme: from profilehooks import profile call = profile(call, immediate=True) # We allow this to fail so we get a # traceback in the browser try: result = call(**self._api_kwargs) except Exception as e: logger.api_error( 'Tautulli APIv2 :: Failed to run %s with %s: %s' % (self._api_cmd, self._api_kwargs, e)) self._api_response_code = 500 if self._api_debug: cherrypy.request.show_tracebacks = True # Reraise the exception so the traceback hits the browser raise self._api_msg = 'Check the logs for errors' ret = None # The api decorated function can return different result types. # convert it to a list/dict before we change it to the users # wanted output try: if isinstance(result, (dict, list)): ret = result else: raise Exception except Exception: try: ret = json.loads(result) except (ValueError, TypeError): try: ret = xmltodict.parse(result, attr_prefix='') except: pass # Fallback if we cant "parse the response" if ret is None: ret = result if (ret is not None or self._api_result_type == 'success') and self._api_authenticated: # To allow override for restart etc # if the call returns some data we are gonna assume its a success self._api_result_type = 'success' self._api_response_code = 200 # Since some of them methods use a api like response for the ui # {result: error, message: 'Some shit happened'} if isinstance(ret, dict): if ret.get('message'): self._api_msg = ret.pop('message', None) if ret.get('result'): self._api_result_type = ret.pop('result', None) if self._api_result_type == 'success' and not self._api_response_code: self._api_response_code = 200 elif self._api_result_type == 'error' and not self._api_response_code: self._api_response_code = 400 if not self._api_response_code: self._api_response_code = 500 cherrypy.response.status = self._api_response_code return self._api_out_as( self._api_responds(result_type=self._api_result_type, msg=self._api_msg, data=ret))
def get_logs(self, sort='', search='', order='desc', regex='', start=0, end=0, **kwargs): """ Get the Tautulli logs. ``` Required parameters: None Optional parameters: sort (str): "time", "thread", "msg", "loglevel" search (str): A string to search for order (str): "desc" or "asc" regex (str): A regex string to search for start (int): Row number to start from end (int): Row number to end at Returns: json: [{"loglevel": "DEBUG", "msg": "Latest version is 2d10b0748c7fa2ee4cf59960c3d3fffc6aa9512b", "thread": "MainThread", "time": "2016-05-08 09:36:51 " }, {...}, {...} ] ``` """ logfile = os.path.join(plexpy.CONFIG.LOG_DIR, logger.FILENAME) templog = [] start = int(start) end = int(end) if regex: logger.api_debug( "Tautulli APIv2 :: Filtering log using regex '%s'" % regex) reg = re.compile(regex, flags=re.I) with open(logfile, 'r') as f: for line in f.readlines(): temp_loglevel_and_time = None try: temp_loglevel_and_time = line.split('- ') loglvl = temp_loglevel_and_time[1].split(' :')[0].strip() tl_tread = line.split(' :: ') if loglvl is None: msg = line.replace('\n', '') else: msg = line.split(' : ')[1].replace('\n', '') thread = tl_tread[1].split(' : ')[0] except IndexError: # We assume this is a traceback tl = (len(templog) - 1) templog[tl]['msg'] += helpers.sanitize( str(line.replace('\n', ''), 'utf-8')) continue if len( line ) > 1 and temp_loglevel_and_time is not None and loglvl in line: d = { 'time': temp_loglevel_and_time[0], 'loglevel': loglvl, 'msg': helpers.sanitize(str(msg.replace('\n', ''), 'utf-8')), 'thread': thread } templog.append(d) if order == 'desc': templog = templog[::-1] if end > 0 or start > 0: logger.api_debug( "Tautulli APIv2 :: Slicing the log from %s to %s" % (start, end)) templog = templog[start:end] if sort: logger.api_debug("Tautulli APIv2 :: Sorting log based on '%s'" % sort) templog = sorted(templog, key=lambda k: k[sort]) if search: logger.api_debug( "Tautulli APIv2 :: Searching log values for '%s'" % search) tt = [ d for d in templog for k, v in d.items() if search.lower() in v.lower() ] if len(tt): templog = tt if regex: tt = [] for l in templog: stringdict = ' '.join('{}{}'.format(k, v) for k, v in l.items()) if reg.search(stringdict): tt.append(l) if len(tt): templog = tt return templog