def _notify(self, title, body, access_token=None, device_iden=None, **kwargs): """ Sends a pushbullet notification based on the provided info or SG config title: The title of the notification to send body: The body string to send access_token: The access token to grant access device_iden: The iden of a specific target, if none provided send to all devices """ access_token = self._choose(access_token, sickbeard.PUSHBULLET_ACCESS_TOKEN) device_iden = self._choose(device_iden, sickbeard.PUSHBULLET_DEVICE_IDEN) # send the request to Pushbullet result = None try: headers = {'Authorization': 'Basic %s' % b64encodestring('%s:%s' % (access_token, '')), 'Content-Type': 'application/json'} resp = requests.post(PUSHAPI_ENDPOINT, headers=headers, data=json.dumps(dict( type='note', title=title, body=body.strip().encode('utf-8'), device_iden=device_iden))) resp.raise_for_status() except (BaseException, Exception): try: # noinspection PyUnboundLocalVariable result = resp.json()['error']['message'] except (BaseException, Exception): result = 'no response' self._log_warning(u'%s' % result) return self._choose((True, 'Failed to send notification: %s' % result)[bool(result)], not bool(result))
def _send_to_xbmc(self, command, host=None, username=None, password=None): """Handles communication to XBMC servers via HTTP API Args: command: Dictionary of field/data pairs, encoded via urllib and passed to the XBMC API via HTTP host: XBMC webserver host:port username: XBMC webserver username password: XBMC webserver password Returns: Returns response.result for successful commands or False if there was an error """ if not host: self._log_debug(u'No host passed, aborting update') return False username = self._choose(username, sickbeard.XBMC_USERNAME) password = self._choose(password, sickbeard.XBMC_PASSWORD) for key in command: if not PY2 or type(command[key]) == text_type: command[key] = command[key].encode('utf-8') enc_command = urlencode(command) self._log_debug(u'Encoded API command: ' + enc_command) url = 'http://%s/xbmcCmds/xbmcHttp/?%s' % (host, enc_command) try: req = urllib.request.Request(url) # if we have a password, use authentication if password: req.add_header( 'Authorization', 'Basic %s' % b64encodestring('%s:%s' % (username, password))) self._log_debug(u'Contacting (with auth header) via url: ' + fixStupidEncodings(url)) else: self._log_debug(u'Contacting via url: ' + fixStupidEncodings(url)) http_response_obj = urllib.request.urlopen( req) # PY2 http_response_obj has no `with` context manager result = decode_str(http_response_obj.read(), sickbeard.SYS_ENCODING) http_response_obj.close() self._log_debug(u'HTTP response: ' + result.replace('\n', '')) return result except (urllib.error.URLError, IOError) as e: self._log_warning(u'Couldn\'t contact HTTP at %s %s' % (fixStupidEncodings(url), ex(e))) return False
def get_devices(access_token=None): # fill in omitted parameters if not access_token: access_token = sickbeard.PUSHBULLET_ACCESS_TOKEN # get devices from pushbullet try: headers = dict(Authorization='Basic %s' % b64encodestring('%s:%s' % (access_token, ''))) return requests.get(DEVICEAPI_ENDPOINT, headers=headers).text except (BaseException, Exception): return json.dumps(dict(error=dict(message='Error failed to connect')))
def solve_ddg_challenge(self, resp, **original_kwargs): parsed_url = urlparse(resp.url) try: submit_url = parsed_url.scheme + ':' + re.findall( '"frm"[^>]+?action="([^"]+)"', resp.text)[0] kwargs = { k: v for k, v in original_kwargs.items() if k not in ['hooks'] } kwargs.setdefault('headers', {}) kwargs.setdefault( 'data', dict(h=b64encodestring( '%s://%s' % (parsed_url.scheme, parsed_url.hostname)), u=b64encodestring(parsed_url.path), p=b64encodestring(parsed_url.port or ''))) self.wait() resp = self.request('POST', submit_url, **kwargs) except (BaseException, Exception): pass return resp
def _send_to_plex(self, command, host, username=None, password=None): """Handles communication to Plex hosts via HTTP API Args: command: Dictionary of field/data pairs, encoded via urllib and passed to the legacy xbmcCmds HTTP API host: Plex host:port username: Plex API username password: Plex API password Returns: Returns True for successful commands or False if there was an error """ if not host: self._log_error(u'No host specified, check your settings') return False for key in command: if not PY2 or type(command[key]) == text_type: command[key] = command[key].encode('utf-8') enc_command = urlencode(command) self._log_debug(u'Encoded API command: ' + enc_command) url = 'http://%s/xbmcCmds/xbmcHttp/?%s' % (host, enc_command) try: req = urllib.request.Request(url) if password: req.add_header( 'Authorization', 'Basic %s' % b64encodestring('%s:%s' % (username, password))) self._log_debug(u'Contacting (with auth header) via url: ' + url) else: self._log_debug(u'Contacting via url: ' + url) http_response_obj = urllib.request.urlopen( req) # PY2 http_response_obj has no `with` context manager result = decode_str(http_response_obj.read(), sickbeard.SYS_ENCODING) http_response_obj.close() self._log_debug(u'HTTP response: ' + result.replace('\n', '')) return True except (urllib.error.URLError, IOError) as e: self._log_warning(u'Couldn\'t contact Plex at ' + fixStupidEncodings(url) + ' ' + ex(e)) return False
def _add_torrent_file(self, result): result.hash = self._request_json( { 'method': 'core.add_torrent_file', 'params': [ '%s.torrent' % result.name, b64encodestring(result.content), { 'move_completed': 'true', 'move_completed_path': sickbeard.TV_DOWNLOAD_DIR } ], 'id': 2 }, True) return result.hash
def send_auth(self, h): if self.username and self.password: h.putheader( 'Authorization', 'Basic %s' % b64encodestring('%s:%s' % (self.username, self.password)))
def send_nzb(search_result): """ :param search_result: search result :type search_result: NZBSearchResult or NZBDataSearchResult :return: """ result = False add_to_top = False nzbget_prio = 0 authed, auth_msg, rpc_client = test_nzbget(sickbeard.NZBGET_HOST, sickbeard.NZBGET_USE_HTTPS, sickbeard.NZBGET_USERNAME, sickbeard.NZBGET_PASSWORD) if not authed: return authed dupekey = '' dupescore = 0 # if it aired recently make it high priority and generate DupeKey/Score for cur_ep_obj in search_result.ep_obj_list: if '' == dupekey: dupekey = 'SickGear-%s%s' % (sickbeard.TVInfoAPI( cur_ep_obj.show_obj.tvid).config.get( 'dupekey', ''), cur_ep_obj.show_obj.prodid) dupekey += '-%s.%s' % (cur_ep_obj.season, cur_ep_obj.episode) if 1 == search_result.priority: add_to_top = True nzbget_prio = sickbeard.NZBGET_PRIORITY if Quality.UNKNOWN != search_result.quality: dupescore = search_result.quality * 100 dupescore += (0, 9 + search_result.properlevel)[0 < search_result.properlevel] nzbcontent64 = None if 'nzbdata' == search_result.resultType: data = search_result.get_data() if not data: return False nzbcontent64 = b64encodestring(data, keep_eol=True) elif 'Anizb' == search_result.provider.name and 'nzb' == search_result.resultType: gen_provider = GenericProvider('') data = gen_provider.get_url(search_result.url) if None is data: return result nzbcontent64 = b64encodestring(data, keep_eol=True) logger.log(u'Sending NZB to NZBGet: %s' % search_result.name) try: # Find out if nzbget supports priority (Version 9.0+), old versions beginning with a 0.x will use the old cmd nzbget_version_str = rpc_client.version() nzbget_version = try_int( nzbget_version_str[:nzbget_version_str.find('.')]) # v13+ has a combined append method that accepts both (url and content) # also the return value has changed from boolean to integer # (Positive number representing NZBID of the queue item. 0 and negative numbers represent error codes.) if 13 <= nzbget_version: nzbget_result = 0 < rpc_client.append( '%s.nzb' % search_result.name, (nzbcontent64, search_result.url)[None is nzbcontent64], sickbeard.NZBGET_CATEGORY, nzbget_prio, False, False, dupekey, dupescore, 'score') elif 12 == nzbget_version: if None is not nzbcontent64: nzbget_result = rpc_client.append( '%s.nzb' % search_result.name, sickbeard.NZBGET_CATEGORY, nzbget_prio, False, nzbcontent64, False, dupekey, dupescore, 'score') else: nzbget_result = rpc_client.appendurl( '%s.nzb' % search_result.name, sickbeard.NZBGET_CATEGORY, nzbget_prio, False, search_result.url, False, dupekey, dupescore, 'score') elif 0 == nzbget_version: if None is not nzbcontent64: nzbget_result = rpc_client.append( '%s.nzb' % search_result.name, sickbeard.NZBGET_CATEGORY, add_to_top, nzbcontent64) else: if 'nzb' == search_result.resultType: gen_provider = GenericProvider('') data = gen_provider.get_url(search_result.url) if None is data: return result nzbcontent64 = b64encodestring(data, keep_eol=True) nzbget_result = rpc_client.append( '%s.nzb' % search_result.name, sickbeard.NZBGET_CATEGORY, add_to_top, nzbcontent64) else: if None is not nzbcontent64: nzbget_result = rpc_client.append( '%s.nzb' % search_result.name, sickbeard.NZBGET_CATEGORY, nzbget_prio, False, nzbcontent64) else: nzbget_result = rpc_client.appendurl( '%s.nzb' % search_result.name, sickbeard.NZBGET_CATEGORY, nzbget_prio, False, search_result.url) if nzbget_result: logger.log(u'NZB sent to NZBGet successfully', logger.DEBUG) result = True else: logger.log( u'NZBGet could not add %s.nzb to the queue' % search_result.name, logger.ERROR) except (BaseException, Exception): logger.log( u'Connect Error to NZBGet: could not add %s.nzb to the queue' % search_result.name, logger.ERROR) return result
def _search_provider(self, search_params, **kwargs): results = [] if not self.url: return results items = {'Cache': [], 'Season': [], 'Episode': [], 'Propers': []} quote_fx = (lambda t: quote(t, safe='~()*!.\'')) for mode in search_params: for search_string in search_params[mode]: search_url = self.url cnt = len(items[mode]) try: for token in self._get_tokens(): if self.should_skip(): return results if not token: continue params = dict(token=token[0], ent=token[1]) if 'Cache' != mode: params.update( {'ss': quote_fx(unidecode(search_string))}) data_json = None vals = [i for i in range(3, 8)] random.SystemRandom().shuffle(vals) for x in vals[0], vals[2], vals[4]: time.sleep(x) params.update(dict(ts=self.ts())) search_url = self.urls[ ('search', 'browse')['Cache' == mode]] % params # decode json below as get resp will false -ve to 'nodata' when no search results html_json = self.get_url(search_url) if None is not html_json: data_json = json.loads(html_json) if data_json or 'Cache' != mode: break if self.should_skip(): return results for item in filter_iter( lambda di: re.match( '(?i).*?(tv|television)', di.get('type', '') or di.get( 'category', '')) and (not self.confirmed or di.get('trusted') or di.get( 'verified')), data_json or {}): seeders, leechers, size = map_list( lambda arg: try_int(*([ item.get(arg[0]) if None is not item.get( arg[0]) else item.get(arg[1]) ]) * 2), (('seeder', 'seed'), ('leecher', 'leech'), ('size', 'size'))) if self._reject_item(seeders, leechers): continue title = item.get('name') or item.get('title') download_url = item.get('magnet') or item.get( 'magnetLink') if not download_url: source = item.get('site') or item.get('source') link = self._link( item.get('url') or item.get('pageLink')) if not source or not link: continue download_url = self.urls['get'] % dict( token=token[0], src=quote_fx(source), url=b64encodestring(quote_fx(link)), ts='%(ts)s') if title and download_url: items[mode].append( (title, download_url, seeders, size)) except generic.HaltParseException: pass except (BaseException, Exception): logger.log( u'Failed to parse. Traceback: %s' % traceback.format_exc(), logger.ERROR) self._log_search(mode, len(items[mode]) - cnt, search_url) results = self._sort_seeding(mode, results + items[mode]) return results
def _add_torrent_file(self, result): return self._add_torrent({'metainfo': b64encodestring(result.content)})
def update_library(self, ep_obj=None, host=None, username=None, password=None, location=None, **kwargs): """Handles updating the Plex Media Server host via HTTP API Plex Media Server currently only supports updating the whole video library and not a specific path. Returns: Returns None for no issue, else a string of host with connection issues """ host = self._choose(host, sickbeard.PLEX_SERVER_HOST) if not host: msg = u'No Plex Media Server host specified, check your settings' self._log_debug(msg) return '%sFail: %s' % (('', '<br>')[self._testing], msg) username = self._choose(username, sickbeard.PLEX_USERNAME) password = self._choose(password, sickbeard.PLEX_PASSWORD) # if username and password were provided, fetch the auth token from plex.tv token_arg = None if username and password: self._log_debug(u'Fetching plex.tv credentials for user: '******'https://plex.tv/users/sign_in.xml', data=b'') req.add_header( 'Authorization', 'Basic %s' % b64encodestring('%s:%s' % (username, password))) req.add_header('X-Plex-Device-Name', 'SickGear') req.add_header('X-Plex-Product', 'SickGear Notifier') req.add_header('X-Plex-Client-Identifier', '5f48c063eaf379a565ff56c9bb2b401e') req.add_header('X-Plex-Version', '1.0') token_arg = False try: http_response_obj = urllib.request.urlopen( req) # PY2 http_response_obj has no `with` context manager auth_tree = XmlEtree.parse(http_response_obj) http_response_obj.close() token = auth_tree.findall('.//authentication-token')[0].text token_arg = '?X-Plex-Token=' + token except urllib.error.URLError as e: self._log( u'Error fetching credentials from plex.tv for user %s: %s' % (username, ex(e))) except (ValueError, IndexError) as e: self._log(u'Error parsing plex.tv response: ' + ex(e)) file_location = location if None is not location else '' if None is ep_obj else ep_obj.location host_validate = self._get_host_list(host, all([token_arg])) hosts_all = {} hosts_match = {} hosts_failed = [] for cur_host in host_validate: response = sickbeard.helpers.get_url('%s/library/sections%s' % (cur_host, token_arg or ''), timeout=10, mute_connect_err=True, mute_read_timeout=True, mute_connect_timeout=True) if response: response = sickbeard.helpers.parse_xml(response) if None is response or not len(response): hosts_failed.append(cur_host) continue sections = response.findall('.//Directory') if not sections: self._log(u'Plex Media Server not running on: ' + cur_host) hosts_failed.append(cur_host) continue for section in filter_iter(lambda x: 'show' == x.attrib['type'], sections): if str(section.attrib['key']) in hosts_all: continue keyed_host = [(str(section.attrib['key']), cur_host)] hosts_all.update(keyed_host) if not file_location: continue for section_location in section.findall('.//Location'): section_path = re.sub( r'[/\\]+', '/', section_location.attrib['path'].lower()) section_path = re.sub(r'^(.{,2})[/\\]', '', section_path) location_path = re.sub(r'[/\\]+', '/', file_location.lower()) location_path = re.sub(r'^(.{,2})[/\\]', '', location_path) if section_path in location_path: hosts_match.update(keyed_host) break if not self._testing: hosts_try = (hosts_all.copy(), hosts_match.copy())[any(hosts_match)] host_list = [] for section_key, cur_host in iteritems(hosts_try): refresh_result = None if not self._testing: refresh_result = sickbeard.helpers.get_url( '%s/library/sections/%s/refresh%s' % (cur_host, section_key, token_arg or '')) if (not self._testing and '' == refresh_result) or self._testing: host_list.append(cur_host) else: hosts_failed.append(cur_host) self._log_error( u'Error updating library section for Plex Media Server: %s' % cur_host) if len(hosts_failed) == len(host_validate): self._log(u'No successful Plex host updated') return 'Fail no successful Plex host updated: %s' % ', '.join( [host for host in hosts_failed]) else: hosts = ', '.join(set(host_list)) if len(hosts_match): self._log( u'Hosts updating where TV section paths match the downloaded show: %s' % hosts) else: self._log(u'Updating all hosts with TV sections: %s' % hosts) return '' hosts = [ host.replace('http://', '') for host in filter_iter( lambda x: x.startswith('http:'), list_values(hosts_all)) ] secured = [ host.replace('https://', '') for host in filter_iter( lambda x: x.startswith('https:'), list_values(hosts_all)) ] failed = ', '.join([ host.replace('http://', '') for host in filter_iter( lambda x: x.startswith('http:'), hosts_failed) ]) failed_secured = ', '.join( filter_iter(lambda x: x not in hosts, [ host.replace('https://', '') for host in filter_iter( lambda x: x.startswith('https:'), hosts_failed) ])) return '<br>' + '<br>'.join([ result for result in [('', 'Fail: username/password when fetching credentials from plex.tv' )[False is token_arg], ('', 'OK (secure connect): %s' % ', '.join(secured))[any(secured)], ('', 'OK%s: %s' % ((' (legacy connect)', '')[None is token_arg], ', '.join(hosts)) )[any(hosts)], ('', 'Fail (secure connect): %s' % failed_secured)[any(failed_secured)], ('', 'Fail%s: %s' % ((' (legacy connect)', '')[None is token_arg], failed))[bool(failed)]] if result ])
def _send_to_xbmc_json(self, command, host=None, username=None, password=None): # type: (...) -> Union[bool, Dict] """Handles communication to XBMC servers via JSONRPC Args: command: Dictionary of field/data pairs, encoded via urllib and passed to the XBMC JSON-RPC via HTTP host: XBMC webserver host:port username: XBMC webserver username password: XBMC webserver password Returns: Returns response.result for successful commands or False if there was an error """ if not host: self._log_debug(u'No host passed, aborting update') return False username = self._choose(username, sickbeard.XBMC_USERNAME) password = self._choose(password, sickbeard.XBMC_PASSWORD) command = command.encode('utf-8') self._log_debug(u'JSON command: ' + command) url = 'http://%s/jsonrpc' % host try: req = urllib.request.Request(url, command) req.add_header('Content-type', 'application/json') # if we have a password, use authentication if password: req.add_header( 'Authorization', 'Basic %s' % b64encodestring('%s:%s' % (username, password))) self._log_debug(u'Contacting (with auth header) via url: ' + fixStupidEncodings(url)) else: self._log_debug(u'Contacting via url: ' + fixStupidEncodings(url)) try: http_response_obj = urllib.request.urlopen( req) # PY2 http_response_obj has no `with` context manager except urllib.error.URLError as e: self._log_warning( u'Error while trying to retrieve API version for "%s": %s' % (host, ex(e))) return False # parse the json result try: result = json.load(http_response_obj) http_response_obj.close() self._log_debug(u'JSON response: ' + str(result)) return result # need to return response for parsing except ValueError: self._log_warning(u'Unable to decode JSON: ' + http_response_obj) return False except IOError as e: self._log_warning(u'Couldn\'t contact JSON API at ' + fixStupidEncodings(url) + ' ' + ex(e)) return False