def __init__(self, name, url, private): self.name = name # urls self._urls = {'base_url': url} # other options self.private = private self.supports_backlog = True self.supports_absolute_numbering = False self.anime_only = False self.search_mode = 'eponly' self.search_fallback = False self.enabled = False self.enable_daily = True self.enable_backlog = True self.cache = TVCache(self) self.proper_strings = ['PROPER|REPACK|REAL|RERIP'] self.search_separator = ' ' # cookies self.enable_cookies = False self.cookies = '' # web session self.session = WebSession(cloudflare=True)
def _request(self, method, url, lang=None, retries=3, **kwargs): self.config['headers'].update({'Content-type': 'application/json'}) self.config['headers']['Authorization'] = 'Bearer {}'.format(self.jwt_token) self.config['headers'].update({'Accept-Language': lang or self.config['language']}) self.config['headers'].update({'Accept': 'application/vnd.thetvdb.v{}'.format(self.config['api']['version'])}) for i in range(0, retries): try: # get response from theTVDB resp = WebSession(cache=self.config['cache_enabled']).request( method, urljoin(self.config['api']['base'], url), headers=self.config['headers'], timeout=sickrage.app.config.indexer_timeout, **kwargs ) except requests.exceptions.HTTPError as e: status_code = e.response.status_code error_message = e.response.text if 'application/json' in e.response.headers.get('content-type', ''): error_message = e.response.json().get('Error', error_message) if status_code == 401: raise tvdb_unauthorized(error_message) if i < retries - 1: continue raise tvdb_error(error_message) return to_lowercase(resp.json())
def _request(self, method, url, lang=None, **kwargs): self.config['headers'].update({'Content-type': 'application/json'}) if self.config['apitoken']: self.config['headers']['authorization'] = 'Bearer {}'.format( self.config['apitoken']) self.config['headers'].update( {'Accept-Language': lang or self.config['language']}) # get response from theTVDB try: resp = WebSession(cache=self.config['cache_enabled']).request( method, urlparse.urljoin(self.config['api']['base'], url), headers=self.config['headers'], timeout=sickrage.app.config.indexer_timeout, **kwargs) except Exception as e: raise tvdb_error(e.message) # handle requests exceptions if resp.status_code == 401: raise tvdb_unauthorized(resp.json()['Error']) elif resp.status_code >= 400: raise tvdb_error(resp.json()['Error']) return to_lowercase(resp.json())
def _notify_emby(self, message, host=None, emby_apikey=None): """Handles notifying Emby host via HTTP API Returns: Returns True for no issue or False if there was an error """ # fill in omitted parameters if not host: host = sickrage.app.config.emby_host if not emby_apikey: emby_apikey = sickrage.app.config.emby_apikey url = 'http://%s/emby/Notifications/Admin' % (host) values = {'Name': 'SiCKRAGE', 'Description': message, 'ImageUrl': 'https://www.sickrage.ca/favicon.ico'} data = json.dumps(values) headers = { 'X-MediaBrowser-Token': emby_apikey, 'Content-Type': 'application/json' } resp = WebSession().get(url, data=data, headers=headers) try: resp.raise_for_status() sickrage.app.log.debug('EMBY: HTTP response: {}'.format(resp.text.replace('\n', ''))) except Exception as e: sickrage.app.log.warning('EMBY: Warning: Couldn\'t contact Emby at {}: {}'.format(url, e)) return False return True
def sendNZB(nzb, session=None): """ Sends an NZB to SABnzbd via the API. :param nzb: The NZBSearchResult object to send to SAB """ show_object = find_show(nzb.show_id, session=session) if not show_object: return False category = sickrage.app.config.sab_category if show_object.is_anime: category = sickrage.app.config.sab_category_anime # if it aired more than 7 days ago, override with the backlog category IDs for episode__number in nzb.episodes: episode_object = show_object.get_episode(nzb.season, episode__number) if datetime.date.today() - episode_object.airdate > datetime.timedelta(days=7): category = sickrage.app.config.sab_category_anime_backlog if episode_object.show.is_anime else sickrage.app.config.sab_category_backlog # set up a dict with the URL params in it params = {'output': 'json'} if sickrage.app.config.sab_username: params['ma_username'] = sickrage.app.config.sab_username if sickrage.app.config.sab_password: params['ma_password'] = sickrage.app.config.sab_password if sickrage.app.config.sab_apikey: params['apikey'] = sickrage.app.config.sab_apikey if category: params['cat'] = category if nzb.priority: params['priority'] = 2 if sickrage.app.config.sab_forced else 1 sickrage.app.log.info('Sending NZB to SABnzbd') url = urljoin(sickrage.app.config.sab_host, 'api') try: jdata = None if nzb.resultType == 'nzb': params['mode'] = 'addurl' params['name'] = nzb.url jdata = WebSession().get(url, params=params, verify=False).json() elif nzb.resultType == 'nzbdata': params['mode'] = 'addfile' multiPartParams = {'nzbfile': (nzb.name + '.nzb', nzb.extraInfo[0])} jdata = WebSession().get(url, params=params, file=multiPartParams, verify=False).json() if not jdata: raise Exception except Exception: sickrage.app.log.info('Error connecting to sab, no data returned') return False sickrage.app.log.debug('Result text from SAB: {}'.format(jdata)) result, error_ = SabNZBd._check_sab_response(jdata) return result
def get_token(self, username=None, password=None, plex_server_token=None): if plex_server_token: self.headers['X-Plex-Token'] = plex_server_token if 'X-Plex-Token' in self.headers: return True if not (username and password): return True sickrage.app.log.debug('PLEX: fetching plex.tv credentials for user: '******'user[login]': username, 'user[password]': password } resp = WebSession().post('https://plex.tv/users/sign_in.json', data=params, headers=self.headers) try: data = resp.json() except ValueError: sickrage.app.log.debug("PLEX: No data returned from plex.tv when attempting to fetch credentials") self.headers.pop('X-Plex-Token', '') return False if data and 'error' in data: sickrage.app.log.debug('PLEX: Error fetching credentials from from plex.tv for user %s: %s' % (username, data['error'])) self.headers.pop('X-Plex-Token', '') return False elif data and 'user' in data: self.headers['X-Plex-Token'] = data['user']['authentication_token'] return 'X-Plex-Token' in self.headers
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 'OK' for successful commands or False if there was an error """ # fill in omitted parameters if not username: username = sickrage.app.config.plex_client_username if not password: password = sickrage.app.config.plex_client_password if not host: sickrage.app.log.warning( 'PLEX: No host specified, check your settings') return False enc_command = urlencode(command) sickrage.app.log.debug('PLEX: Encoded API command: ' + enc_command) url = 'http://%s/xbmcCmds/xbmcHttp/?%s' % (host, enc_command) headers = {} # if we have a password, use authentication if password: base64string = base64.b64encode( bytes('{}:{}'.format(username, password).replace('\n', ''), 'utf-8')) authheader = "Basic {}".format(base64string.decode('ascii')) headers['Authorization'] = authheader sickrage.app.log.debug( 'PLEX: Contacting (with auth header) via url: ' + url) else: sickrage.app.log.debug('PLEX: Contacting via url: ' + url) try: resp = WebSession().get(url, headers=headers) resp.raise_for_status() result = resp.text sickrage.app.log.debug('PLEX: HTTP response: ' + result.replace('\n', '')) # could return result response = re.compile('<html><li>(.+\w)</html>').findall(result) return 'OK' except Exception as e: sickrage.app.log.warning( 'PLEX: Warning: Couldn\'t contact Plex at {}: {}'.format( url, e)) return False
def update_network_dict(): """Update timezone information from SR repositories""" url = 'https://cdn.sickrage.ca/network_timezones/' try: url_data = WebSession().get(url).text except Exception: sickrage.app.log.warning( 'Updating network timezones failed, this can happen from time to time. URL: %s' % url) return d = {} try: for line in url_data.splitlines(): (key, val) = line.strip().rsplit(':', 1) if key is None or val is None: continue d[key] = val except (IOError, OSError): pass queries = [] for network, timezone in d.items(): existing = network in network_dict if not existing: try: sickrage.app.cache_db.get('network_timezones', network) except RecordNotFound: sickrage.app.cache_db.insert({ '_t': 'network_timezones', 'network_name': ss(network), 'timezone': timezone }) elif network_dict[network] is not timezone: try: dbData = sickrage.app.cache_db.get('network_timezones', network) dbData['timezone'] = timezone sickrage.app.cache_db.update(dbData) except RecordNotFound: continue if existing: del network_dict[network] for x in network_dict: try: sickrage.app.cache_db.delete( sickrage.app.cache_db.get('network_timezones', x)) except RecordNotFound: continue load_network_dict()
def _check_for_new_version(self): from distutils.version import StrictVersion url = "https://pypi.python.org/pypi/{}/json".format('sickrage') resp = WebSession().get(url) versions = resp.json()["releases"].keys() versions.sort(key=StrictVersion, reverse=True) try: return versions[0] except Exception: return self._find_installed_version()
def _check_for_new_version(self): from distutils.version import LooseVersion url = "https://pypi.org/pypi/{}/json".format('sickrage') resp = WebSession().get(url) versions = resp.json()["releases"].keys() versions = [x for x in versions if 'dev' not in x] versions.sort(key=LooseVersion, reverse=True) try: return versions[0] except Exception: return self._find_installed_version()
def _send_to_kodi(self, command, host=None, username=None, password=None): """Handles communication to KODI servers via HTTP API Args: command: Dictionary of field/data pairs, encoded via urllib and passed to the KODI API via HTTP host: KODI webserver host:port username: KODI webserver username password: KODI webserver password Returns: Returns response.result for successful commands or False if there was an error """ # fill in omitted parameters if not username: username = sickrage.app.config.kodi_username if not password: password = sickrage.app.config.kodi_password if not host: sickrage.app.log.warning('No KODI host passed, aborting update') return False enc_command = urlencode(command) sickrage.app.log.debug("KODI encoded API command: " + enc_command) url = 'http://%s/kodiCmds/kodiHttp/?%s' % (host, enc_command) headers = {} # if we have a password, use authentication if password: authheader = "Basic {}".format( base64.b64encode( bytes('{}:{}'.format(username, password).replace('\n', ''), 'utf-8')).decode('ascii')) headers["Authorization"] = authheader sickrage.app.log.debug( "Contacting KODI (with auth header) via url: " + url) else: sickrage.app.log.debug("Contacting KODI via url: " + url) try: result = WebSession().get(url, headers=headers).text except Exception as e: sickrage.app.log.debug("Couldn't contact KODI HTTP at %r : %r" % (url, e)) return False sickrage.app.log.debug("KODI HTTP response: " + result.replace('\n', '')) return result
def _sendFreeMobileSMS(self, title, msg, id=None, apiKey=None): """ Sends a SMS notification msg: The message to send (unicode) title: The title of the message userKey: The pushover user id to send the message to (or to subscribe with) returns: True if the message succeeded, False otherwise """ if id is None: id = sickrage.app.config.freemobile_id if apiKey is None: apiKey = sickrage.app.config.freemobile_apikey sickrage.app.log.debug("Free Mobile in use with API KEY: " + apiKey) # build up the URL and parameters msg = msg.strip() msg_quoted = parse.quote(title + ": " + msg) URL = "https://smsapi.free-mobile.fr/sendmsg?user="******"&pass="******"&msg=" + msg_quoted resp = WebSession().get(URL) # send the request to Free Mobile try: resp.raise_for_status() except Exception as e: if resp.status_code == 400: message = "Missing parameter(s)." sickrage.app.log.error(message) return False, message if resp.status_code == 402: message = "Too much SMS sent in a short time." sickrage.app.log.error(message) return False, message if resp.status_code == 403: message = "API service isn't enabled in your account or ID / API key is incorrect." sickrage.app.log.error(message) return False, message if resp.status_code == 500: message = "Server error. Please retry in few moment." sickrage.app.log.error(message) return False, message message = "Error while sending SMS: {}".format(e) sickrage.app.log.error(message) return False, message message = "Free Mobile SMS successful." sickrage.app.log.info(message) return True, message
def _check_for_new_version(self): from distutils.version import LooseVersion url = "https://pypi.org/pypi/{}/json".format('sickrage') try: resp = WebSession().get(url) versions = resp.json()["releases"].keys() versions = [x for x in versions if 'dev' not in x] versions.sort(key=LooseVersion, reverse=True) return versions[0] except Exception: return self._find_installed_version()
def update_network_timezones(self): """Update timezone information from SR repositories""" session = sickrage.app.cache_db.session() network_timezones = {} try: url_data = WebSession().get('https://cdn.sickrage.ca/network_timezones/').text except Exception: sickrage.app.log.warning('Updating network timezones failed.') return try: for line in url_data.splitlines(): (key, val) = line.strip().rsplit(':', 1) if all([key, val]): network_timezones[key] = val except (IOError, OSError): pass for x in session.query(CacheDB.NetworkTimezone): if x.network_name not in network_timezones: session.query(CacheDB.NetworkTimezone).filter_by(network_name=x.network_name).delete() session.commit() sql_to_add = [] sql_to_update = [] for network, timezone in network_timezones.items(): try: dbData = session.query(CacheDB.NetworkTimezone).filter_by(network_name=network).one() if dbData.timezone != timezone: dbData.timezone = timezone sql_to_update.append(dbData.as_dict()) except orm.exc.NoResultFound: sql_to_add.append({ 'network_name': network, 'timezone': timezone }) if len(sql_to_add): session.bulk_insert_mappings(CacheDB.NetworkTimezone, sql_to_add) session.commit() if len(sql_to_update): session.bulk_update_mappings(CacheDB.NetworkTimezone, sql_to_update) session.commit() # cleanup del network_timezones
def notify_settings(self, host, dbloc, instance): """ Retrieves the NMJv2 database location from Popcorn hour host: The hostname/IP of the Popcorn Hour server dbloc: 'local' for PCH internal harddrive. 'network' for PCH network shares instance: Allows for selection of different DB in case of multiple databases Returns: True if the settings were retrieved successfully, False otherwise """ url_loc = "http://" + host + ":8008/file_operation?arg0=list_user_storage_file&arg1=&arg2=" + instance + "&arg3=20&arg4=true&arg5=true&arg6=true&arg7=all&arg8=name_asc&arg9=false&arg10=false" try: resp = WebSession().get(url_loc) response1 = resp.text xml = parseString(response1) time.sleep(300.0 / 1000.0) for node in xml.getElementsByTagName('path'): xmlTag = node.toxml() xmlData = xmlTag.replace('<path>', '').replace('</path>', '').replace('[=]', '') url_db = "http://" + host + ":8008/metadata_database?arg0=check_database&arg1=" + xmlData respdb = WebSession().get(url_db) xmldb = parseString(respdb.text) returnvalue = xmldb.getElementsByTagName( 'returnValue')[0].toxml().replace('<returnValue>', '').replace( '</returnValue>', '') if returnvalue == "0": DB_path = xmldb.getElementsByTagName( 'database_path')[0].toxml().replace( '<database_path>', '').replace('</database_path>', '').replace('[=]', '') if dbloc == "local" and DB_path.find("localhost") > -1: sickrage.app.config.nmjv2_host = host sickrage.app.config.nmjv2_database = DB_path return True if dbloc == "network" and DB_path.find("://") > -1: sickrage.app.config.nmjv2_host = host sickrage.app.config.nmjv2_database = DB_path return True except Exception as e: sickrage.app.log.warning( "Warning: Couldn't contact popcorn hour on host %s: %s" % (host, e)) return False
def __init__(self, name, host=None, username=None, password=None): self.name = name self.username = sickrage.app.config.torrent_username if not username else username self.password = sickrage.app.config.torrent_password if not password else password self.host = sickrage.app.config.torrent_host if not host else host self.rpcurl = sickrage.app.config.torrent_rpcurl self.url = None self.auth = None self.last_time = time.time() self.session = WebSession(cache=False) self._response = None
def _sendProwl(self, prowl_api=None, prowl_priority=None, event=None, message=None, force=False): if not sickrage.app.config.use_prowl and not force: return False if prowl_api is None: prowl_api = sickrage.app.config.prowl_api if prowl_priority is None: prowl_priority = sickrage.app.config.prowl_priority title = "SiCKRAGE" sickrage.app.log.debug( "PROWL: Sending notice with details: event=\"%s\", message=\"%s\", priority=%s, api=%s" % (event, message, prowl_priority, prowl_api)) data = { 'apikey': prowl_api, 'application': title, 'event': event, 'description': message, 'priority': prowl_priority } resp = WebSession().post( "https://api.prowlapp.com/publicapi/add", headers={'Content-type': "application/x-www-form-urlencoded"}, data=urlencode(data)) try: resp.raise_for_status() request_status = resp.status_code if request_status == 200: sickrage.app.log.info("Prowl notifications sent.") return True elif request_status == 401: sickrage.app.log.error("Prowl auth failed: %s" % resp.reason) return False else: sickrage.app.log.error("Prowl notification failed.") return False except Exception: sickrage.app.log.error("Prowl notification failed.") return False
def _sendBoxcar2(self, msg, title, accesstoken): """ Sends a boxcar2 notification to the address provided msg: The message to send title: The title of the message accesstoken: to send to this device returns: True if the message succeeded, False otherwise """ # build up the URL and parameters more info goes here - # https://boxcar.uservoice.com/knowledgebase/articles/306788-how-to-send-your-boxcar-account-a-notification msg = msg.strip() data = urlencode({ 'user_credentials': accesstoken, 'notification[title]': "SiCKRAGE : " + title + ' : ' + msg, 'notification[long_message]': msg, 'notification[sound]': "notifier-2" }) # send the request to boxcar2 resp = WebSession().get(API_URL, data=data, timeout=60) try: resp.raise_for_status() except RequestException as e: # if we get an error back that doesn't have an error code then who knows what's really happening sickrage.app.log.warning( "Boxcar2 notification failed. Error code: {}".format( resp.status_code)) # HTTP status 404 if resp.status_code == 404: sickrage.app.log.warning("Access token is invalid. Check it.") return False # If you receive an HTTP status code of 400, it is because you failed to send the proper parameters elif resp.status_code == 400: sickrage.app.log.error("Wrong data send to boxcar2") return False sickrage.app.log.debug("Boxcar2 notification successful.") return True
def install_requirements(self, branch): requirements_url = "https://git.sickrage.ca/SiCKRAGE/sickrage/raw/{}/requirements.txt".format(branch) requirements_file = tempfile.NamedTemporaryFile(delete=False) try: requirements_file.write(WebSession().get(requirements_url).content) except Exception: requirements_file.close() os.unlink(requirements_file.name) return False output, __, exit_status = self._pip_cmd(self._pip3_path, 'install --no-cache-dir -r {}'.format(requirements_file.name)) if exit_status != 0: __, __, exit_status = self._pip_cmd(self._pip3_path, 'install --no-cache-dir --user -r {}'.format(requirements_file.name)) if exit_status == 0: requirements_file.close() os.unlink(requirements_file.name) return True sickrage.app.alerts.error(_('Updater'), _('Failed to update requirements')) sickrage.app.log.warning('Unable to update requirements') if output: output = output.decode("utf-8", "ignore").strip() if isinstance(output, bytes) else output.strip() sickrage.app.log.debug("PIP CMD OUTPUT: {}".format(output)) requirements_file.close() os.unlink(requirements_file.name) return False
def _xem_exceptions_fetcher(force=False): if should_refresh('xem') or force: sickrage.app.log.info("Checking for XEM scene exception updates") for indexer in IndexerApi().indexers: url = "http://thexem.de/map/allNames?origin=%s&seasonNumbers=1" % IndexerApi(indexer).config[ 'xem_origin'] try: parsedJSON = WebSession().get(url, timeout=90).json() except Exception: sickrage.app.log.debug("Check scene exceptions update failed for " + IndexerApi( indexer).name + ", Unable to get URL: " + url) continue if parsedJSON['result'] == 'failure': continue for indexer_id, names in parsedJSON['data'].items(): try: xem_exception_dict[int(indexer_id)] = names except Exception as e: sickrage.app.log.warning( "XEM: Rejected entry: indexer_id:{0}; names:{1}".format(indexer_id, names)) sickrage.app.log.debug("XEM: Rejected entry error message:{}".format(e)) set_last_refresh('xem') for xem_ex in xem_exception_dict: if xem_ex in exception_dict: exception_dict[xem_ex] = exception_dict[xem_ex] + xem_exception_dict[xem_ex] else: exception_dict[xem_ex] = xem_exception_dict[xem_ex] return xem_exception_dict
def _check_for_new_version(self): git_version_url = "https://git.sickrage.ca/SiCKRAGE/sickrage/raw/{}/sickrage/version.txt" try: return WebSession().get(git_version_url.format(('master', 'develop')['dev' in self.version])).text except Exception: return self._find_installed_version()
def test_authentication(host=None, username=None, password=None, apikey=None): """ Sends a simple API request to SAB to determine if the given connection information is connect :param host: The host where SAB is running (incl port) :param username: The username to use for the HTTP request :param password: The password to use for the HTTP request :param apikey: The API key to provide to SAB :return: A tuple containing the success boolean and a message """ # build up the URL parameters params = { 'mode': 'queue', 'output': 'json', 'ma_username': username, 'ma_password': password, 'apikey': apikey } url = urljoin(host, 'api') data = WebSession().get(url, params=params, verify=False).json() if not data: return False, data # check the result and determine if it's good or not result, sabText = SabNZBd._check_sab_response(data) if not result: return False, sabText return True, 'Success'
def _send_telegram_msg(self, title, msg, id=None, api_key=None): """ Sends a Telegram notification :param title: The title of the notification to send :param msg: The message string to send :param id: The Telegram user/group id to send the message to :param api_key: Your Telegram bot API token :returns: True if the message succeeded, False otherwise """ id = sickrage.app.config.telegram_id or id api_key = sickrage.app.config.telegram_apikey or api_key payload = {'chat_id': id, 'text': '{} : {}'.format(title, msg)} telegram_api = 'https://api.telegram.org/bot{}/{}' try: resp = WebSession().post(telegram_api.format( api_key, 'sendMessage'), json=payload).json() success = resp['ok'] message = 'Telegram message sent successfully.' if success else '{} {}'.format( resp['error_code'], resp['description']) except Exception as e: success = False message = 'Error while sending Telegram message: {} '.format(e) sickrage.app.log.info(message) return success, message
def _sendPushalot(self, pushalot_authorizationtoken=None, event=None, message=None, force=False): if not sickrage.app.config.pushalot.enable and not force: return False sickrage.app.log.debug("Pushalot event: " + event) sickrage.app.log.debug("Pushalot message: " + message) sickrage.app.log.debug("Pushalot api: " + pushalot_authorizationtoken) data = {'AuthorizationToken': pushalot_authorizationtoken, 'Title': event, 'Body': message} try: WebSession().post("https://pushalot.com/api/sendmessage", headers={'Content-type': "application/x-www-form-urlencoded"}, data=urlencode(data)) except requests.exceptions.HTTPError as e: if e.response.status_code == 410: sickrage.app.log.warning("Pushalot auth failed: %s" % e.response.text) return False sickrage.app.log.error("Pushalot notification failed.") return False sickrage.app.log.debug("Pushalot notifications sent.") return True
def _check_for_new_version(self): git_version_url = "https://git.sickrage.ca/SiCKRAGE/sickrage/raw/master/sickrage/version.txt" try: return WebSession().get(git_version_url).text except Exception: return self._find_installed_version()
def fetch_popular_shows(self): """Get popular show information from IMDB""" popular_shows = [] try: data = WebSession().get(self.url, headers={ 'Referer': 'http://akas.imdb.com/' }, params=self.params).text except Exception: return None with bs4_parser(data) as soup: for row in soup.find_all("div", {"class": "lister-item"}): show = {} image_div = row.find("div", {"class": "lister-item-image"}) if image_div: image = image_div.find("img") show['image_url_large'] = self.change_size( image['loadlate'], 3) show['imdb_tt'] = image['data-tconst'] show['image_path'] = posixpath.join( 'images', 'imdb_popular', os.path.basename(show['image_url_large'])) self.cache_image(show['image_url_large']) content = row.find("div", {"class": "lister-item-content"}) if content: header = row.find("h3", {"class": "lister-item-header"}) if header: a_tag = header.find("a") if a_tag: show['name'] = a_tag.get_text(strip=True) show['imdb_url'] = "http://www.imdb.com" + a_tag[ "href"] show['year'] = header.find( "span", { "class": "lister-item-year" }).contents[0].split(" ")[0][1:5] imdb_rating = row.find("div", {"class": "ratings-imdb-rating"}) show['rating'] = imdb_rating[ 'data-value'] if imdb_rating else None votes = row.find("span", {"name": "nv"}) show['votes'] = votes['data-value'] if votes else None outline = content.find_all("p", {"class": "text-muted"}) if outline and len(outline) >= 2: show['outline'] = outline[1].contents[0].strip("\"") else: show['outline'] = '' popular_shows.append(show) return popular_shows
def _sendPushbullet(self, pushbullet_api=None, pushbullet_device=None, event=None, message=None, force=False): if not (sickrage.app.config.use_pushbullet or force): return False pushbullet_api = pushbullet_api or sickrage.app.config.pushbullet_api pushbullet_device = pushbullet_device or sickrage.app.config.pushbullet_device sickrage.app.log.debug("Pushbullet event: %r" % event) sickrage.app.log.debug("Pushbullet message: %r" % message) sickrage.app.log.debug("Pushbullet api: %r" % pushbullet_api) sickrage.app.log.debug("Pushbullet devices: %r" % pushbullet_device) post_data = { 'title': event.encode('utf-8'), 'body': message.encode('utf-8'), 'type': 'note' } if pushbullet_device: post_data['device_iden'] = pushbullet_device.encode('utf8') headers = { 'Content-Type': 'application/json', 'Access-Token': pushbullet_api } try: response = WebSession().post(urljoin(self.url, 'pushes'), data=json.dumps(post_data), headers=headers) except Exception: sickrage.app.log.debug( 'Pushbullet authorization failed with exception: %r' % traceback.format_exc()) return False if response.status_code == 410: sickrage.app.log.debug('Pushbullet authorization failed') return False if not response.ok: sickrage.app.log.debug( 'Pushbullet call failed with error code %r' % response.status_code) return False sickrage.app.log.debug("Pushbullet response: %r" % response.text) if not response.text: sickrage.app.log.error("Pushbullet notification failed.") return False sickrage.app.log.debug("Pushbullet notifications sent.") return (True, response.text)[event is self.TEST_EVENT or event is None]
def _send_to_kodi_json(self, command, host=None, username=None, password=None): """Handles communication to KODI servers via JSONRPC Args: command: Dictionary of field/data pairs, encoded via urllib and passed to the KODI JSON-RPC via HTTP host: KODI webserver host:port username: KODI webserver username password: KODI webserver password Returns: Returns response.result for successful commands or False if there was an error """ # fill in omitted parameters if not username: username = sickrage.app.config.kodi_username if not password: password = sickrage.app.config.kodi_password if not host: sickrage.app.log.warning('No KODI host passed, aborting update') return False sickrage.app.log.debug("KODI JSON command: {!r}".format(command)) url = 'http://%s/jsonrpc' % host headers = {"Content-type": "application/json"} # if we have a password, use authentication if password: authheader = "Basic {}".format( base64.b64encode( bytes('{}:{}'.format(username, password).replace('\n', ''), 'utf-8')).decode('ascii')) headers["Authorization"] = authheader sickrage.app.log.debug( "Contacting KODI (with auth header) via url: " + url) else: sickrage.app.log.debug("Contacting KODI via url: " + url) try: result = WebSession().post(url, json=command, headers=headers).json() sickrage.app.log.debug("KODI JSON response: " + str(result)) return result except Exception as e: if sickrage.app.config.kodi_always_on: sickrage.app.log.warning( "Warning: Couldn't contact KODI JSON API at " + url + " {}".format(e)) return False
def _request(self, method, url, lang=None, retries=3, **kwargs): self.config['headers'].update({'Content-type': 'application/json'}) self.config['headers']['Authorization'] = 'Bearer {}'.format(self.jwt_token) self.config['headers'].update({'Accept-Language': lang or self.config['language']}) for i in range(0, retries): try: # get response from theTVDB resp = WebSession(cache=self.config['cache_enabled']).request( method, urljoin(self.config['api']['base'], url), headers=self.config['headers'], timeout=sickrage.app.config.indexer_timeout, **kwargs ) except Exception as e: if i < retries - 1: continue raise tvdb_error(e) # handle requests exceptions try: if resp.status_code == 401: raise tvdb_unauthorized(resp.json()['Error']) elif resp.status_code >= 400: if i < retries - 1: continue raise tvdb_error(resp.json()['Error']) except JSONDecodeError: try: resp.raise_for_status() except RequestException as e: if i < retries - 1: continue raise tvdb_error(e) return to_lowercase(resp.json())
def getRSSFeed(self, url, params=None): try: if self.provider.login(): resp = WebSession().get(url, params=params).text return feedparser.parse(resp) except Exception as e: sickrage.app.log.debug("RSS Error: {}".format(e)) return feedparser.FeedParserDict()
def update(self): """ Downloads the latest source tarball from server and installs it over the existing version. """ tar_download_url = 'https://git.sickrage.ca/SiCKRAGE/sickrage/repository/archive.tar.gz?ref={}'.format(('master', 'develop')['dev' in self.version]) try: if not self.install_requirements(self.current_branch): return False with tempfile.TemporaryFile() as update_tarfile: sickrage.app.log.info("Downloading update from " + repr(tar_download_url)) update_tarfile.write(WebSession().get(tar_download_url).content) update_tarfile.seek(0) with tempfile.TemporaryDirectory(prefix='sr_update_', dir=sickrage.app.data_dir) as unpack_dir: sickrage.app.log.info("Extracting SiCKRAGE update file") try: tar = tarfile.open(fileobj=update_tarfile, mode='r:gz') tar.extractall(unpack_dir) tar.close() except tarfile.ReadError: sickrage.app.log.warning("Invalid update data, update failed: not a gzip file") return False # find update dir name update_dir_contents = [x for x in os.listdir(unpack_dir) if os.path.isdir(os.path.join(unpack_dir, x))] if len(update_dir_contents) != 1: sickrage.app.log.warning("Invalid update data, update failed: " + str(update_dir_contents)) return False # walk temp folder and move files to main folder content_dir = os.path.join(unpack_dir, update_dir_contents[0]) sickrage.app.log.info("Moving files from " + content_dir + " to " + sickrage.MAIN_DIR) for dirname, __, filenames in os.walk(content_dir): dirname = dirname[len(content_dir) + 1:] for curfile in filenames: old_path = os.path.join(content_dir, dirname, curfile) new_path = os.path.join(sickrage.MAIN_DIR, dirname, curfile) if os.path.isfile(new_path) and os.path.exists(new_path): os.remove(new_path) try: shutil.move(old_path, new_path) except IOError: os.makedirs(os.path.dirname(new_path)) shutil.move(old_path, new_path) except Exception as e: sickrage.app.log.error("Error while trying to update: {}".format(e)) return False # Notify update successful Notifiers.mass_notify_version_update(self.get_newest_version) return True
def _request(self, method, url, lang=None, **kwargs): self.config['headers'].update({'Content-type': 'application/json'}) self.config['headers']['Authorization'] = 'Bearer {}'.format(self.jwt_token) self.config['headers'].update({'Accept-Language': lang or self.config['language']}) # get response from theTVDB try: resp = WebSession(cache=self.config['cache_enabled']).request( method, urlparse.urljoin(self.config['api']['base'], url), headers=self.config['headers'], timeout=sickrage.app.config.indexer_timeout, **kwargs ) except Exception as e: raise tvdb_error(str(e)) # handle requests exceptions try: if resp.status_code == 401: raise tvdb_unauthorized(resp.json()['Error']) elif resp.status_code >= 400: raise tvdb_error(resp.json()['Error']) except JSONDecodeError: try: resp.raise_for_status() except RequestException as e: raise tvdb_error(str(e)) return to_lowercase(resp.json())
def update_network_timezones(self): """Update timezone information from SR repositories""" network_timezones = {} try: url_data = WebSession().get('https://cdn.sickrage.ca/network_timezones/').text except Exception: sickrage.app.log.warning('Updating network timezones failed.') return try: for line in url_data.splitlines(): (key, val) = line.strip().rsplit(':', 1) if all([key, val]): network_timezones[key] = val except (IOError, OSError): pass for x in sickrage.app.cache_db.all('network_timezones'): if x['network_name'] not in network_timezones: sickrage.app.cache_db.delete(x) for network, timezone in network_timezones.items(): dbData = sickrage.app.cache_db.get('network_timezones', network) if not dbData: sickrage.app.cache_db.insert({ '_t': 'network_timezones', 'network_name': ss(network), 'timezone': timezone }) elif dbData['timezone'] != timezone: dbData['timezone'] = timezone sickrage.app.cache_db.update(dbData) # cleanup del network_timezones