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 """ # fill in omitted parameters if not username: username = sickbeard.XBMC_USERNAME if not password: password = sickbeard.XBMC_PASSWORD if not host: logger.log(u'No XBMC host passed, aborting update', logger.DEBUG) return False for key in command: if type(command[key]) == unicode: command[key] = command[key].encode('utf-8') enc_command = urllib.urlencode(command) logger.log(u"XBMC encoded API command: " + enc_command, logger.DEBUG) url = 'http://%s/xbmcCmds/xbmcHttp/?%s' % (host, enc_command) try: req = urllib2.Request(url) # if we have a password, use authentication if password: base64string = base64.encodestring('%s:%s' % (username, password))[:-1] authheader = "Basic %s" % base64string req.add_header("Authorization", authheader) logger.log( u"Contacting XBMC (with auth header) via url: " + toUnicode(url), logger.DEBUG) else: logger.log(u"Contacting XBMC via url: " + toUnicode(url), logger.DEBUG) response = urllib2.urlopen(req) result = response.read().decode(sickbeard.SYS_ENCODING) response.close() logger.log(u"XBMC HTTP response: " + result.replace('\n', ''), logger.DEBUG) return result except (urllib2.URLError, IOError), e: logger.log( u"Warning: Couldn't contact XBMC HTTP at " + toUnicode(url) + " " + ex(e), logger.WARNING) return False
def ex(e): """ Returns a unicode string from the exception text if it exists. """ e_message = u"" if not e or not e.args: return e_message for arg in e.args: if arg is not None: if isinstance(arg, (str, unicode)): fixed_arg = toUnicode(arg) else: try: fixed_arg = u"error " + toUnicode(str(arg)) except: fixed_arg = None if fixed_arg: if not e_message: e_message = fixed_arg else: e_message = e_message + " : " + fixed_arg return e_message
def _send_to_xbmc_json(self, command, host=None, username=None, password=None): """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 """ # fill in omitted parameters if not username: username = sickbeard.XBMC_USERNAME if not password: password = sickbeard.XBMC_PASSWORD if not host: logger.log(u'No XBMC host passed, aborting update', logger.DEBUG) return False command = command.encode('utf-8') logger.log(u"XBMC JSON command: " + command, logger.DEBUG) url = 'http://%s/jsonrpc' % (host) try: req = urllib2.Request(url, command) req.add_header("Content-type", "application/json") # if we have a password, use authentication if password: base64string = base64.encodestring('%s:%s' % (username, password))[:-1] authheader = "Basic %s" % base64string req.add_header("Authorization", authheader) logger.log(u"Contacting XBMC (with auth header) via url: " + toUnicode(url), logger.DEBUG) else: logger.log(u"Contacting XBMC via url: " + toUnicode(url), logger.DEBUG) try: response = urllib2.urlopen(req) except urllib2.URLError, e: logger.log(u"Error while trying to retrieve XBMC API version for " + host + ": " + ex(e), logger.WARNING) return False # parse the json result try: result = json.load(response) response.close() logger.log(u"XBMC JSON response: " + str(result), logger.DEBUG) return result # need to return response for parsing except ValueError, e: logger.log(u"Unable to decode JSON: " + response, logger.WARNING) return False
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 """ # fill in omitted parameters if not username: username = sickbeard.XBMC_USERNAME if not password: password = sickbeard.XBMC_PASSWORD if not host: logger.log(u'No XBMC host passed, aborting update', logger.DEBUG) return False for key in command: if type(command[key]) == unicode: command[key] = command[key].encode('utf-8') enc_command = urllib.urlencode(command) logger.log(u"XBMC encoded API command: " + enc_command, logger.DEBUG) url = 'http://%s/xbmcCmds/xbmcHttp/?%s' % (host, enc_command) try: req = urllib2.Request(url) # if we have a password, use authentication if password: base64string = base64.encodestring('%s:%s' % (username, password))[:-1] authheader = "Basic %s" % base64string req.add_header("Authorization", authheader) logger.log(u"Contacting XBMC (with auth header) via url: " + toUnicode(url), logger.DEBUG) else: logger.log(u"Contacting XBMC via url: " + toUnicode(url), logger.DEBUG) response = urllib2.urlopen(req) result = response.read().decode(sickbeard.SYS_ENCODING) response.close() logger.log(u"XBMC HTTP response: " + result.replace('\n', ''), logger.DEBUG) return result except (urllib2.URLError, IOError), e: logger.log(u"Warning: Couldn't contact XBMC HTTP at " + toUnicode(url) + " " + ex(e), logger.WARNING) return False
def notify_download(self, ep_name, title="Completed:"): """ Send a notification that an episode was downloaded ep_name: The name of the episode that was downloaded title: The title of the notification (optional) """ ep_name = toUnicode(ep_name) if sickbeard.EMAIL_NOTIFY_ONDOWNLOAD: show = self._parseEp(ep_name) to = self._generate_recepients(show) if len(to) == 0: logger.log('Skipping email notify because there are no configured recepients', logger.WARNING) else: try: msg = MIMEMultipart('alternative') msg.attach(MIMEText( "<body style='font-family:Helvetica, Arial, sans-serif;'><h3>SickRage Notification - Downloaded</h3>\n<p>Show: <b>" + re.search( "(.+?) -.+", ep_name).group(1) + "</b></p>\n<p>Episode: <b>" + re.search( ".+ - (.+?-.+) -.+", ep_name).group( 1) + "</b></p>\n\n<footer style='margin-top: 2.5em; padding: .7em 0; color: #777; border-top: #BBB solid 1px;'>Powered by SickRage.</footer></body>", 'html')) except: msg = MIMEText(ep_name) msg['Subject'] = 'Downloaded: ' + ep_name msg['From'] = sickbeard.EMAIL_FROM msg['To'] = ','.join(to) if self._sendmail(sickbeard.EMAIL_HOST, sickbeard.EMAIL_PORT, sickbeard.EMAIL_FROM, sickbeard.EMAIL_TLS, sickbeard.EMAIL_USER, sickbeard.EMAIL_PASSWORD, to, msg): logger.log("Download notification sent to [%s] for '%s'" % (to, ep_name), logger.DEBUG) else: logger.log("Download notification ERROR: %s" % self.last_err, logger.ERROR)
def _parseEp(self, ep_name): ep_name = toUnicode(ep_name) sep = " - " titles = ep_name.split(sep) titles.sort(key=len, reverse=True) logger.log("TITLES: %s" % titles, logger.DEBUG) return titles
def _logHistoryItem(action, showid, season, episode, quality, resource, provider, version=-1): logDate = datetime.datetime.today().strftime(dateFormat) resource = toUnicode(resource) myDB = db.DBConnection() myDB.action( "INSERT INTO history (action, date, showid, season, episode, quality, resource, provider, version) VALUES (?,?,?,?,?,?,?,?,?)", [action, logDate, showid, season, episode, quality, resource, provider, version])
def createNZBString(fileElements, xmlns): rootElement = etree.Element("nzb") if xmlns: rootElement.set("xmlns", xmlns) for curFile in fileElements: rootElement.append(stripNS(curFile, xmlns)) return xml.etree.ElementTree.tostring(toUnicode(rootElement))
def prepareFailedName(release): """Standardizes release name for failed DB""" fixed = urllib.unquote(release) if (fixed.endswith(".nzb")): fixed = fixed.rpartition(".")[0] fixed = re.sub("[\.\-\+\ ]", "_", fixed) fixed = toUnicode(fixed) return fixed
def _addCacheEntry(self, name, url, parse_result=None, indexer_id=0): # check if we passed in a parsed result or should we try and create one if not parse_result: # create showObj from indexer_id if available showObj=None if indexer_id: showObj = helpers.findCertainShow(sickbeard.showList, indexer_id) try: myParser = NameParser(showObj=showObj, convert=True) parse_result = myParser.parse(name) except InvalidNameException: logger.log(u"Unable to parse the filename " + name + " into a valid episode", logger.DEBUG) return None except InvalidShowException: logger.log(u"Unable to parse the filename " + name + " into a valid show", logger.DEBUG) return None if not parse_result or not parse_result.series_name: return None # if we made it this far then lets add the parsed result to cache for usager later on season = parse_result.season_number if parse_result.season_number else 1 episodes = parse_result.episode_numbers if season and episodes: # store episodes as a seperated string episodeText = "|" + "|".join(map(str, episodes)) + "|" # get the current timestamp curTimestamp = int(time.mktime(datetime.datetime.today().timetuple())) # get quality of release quality = parse_result.quality name = toUnicode(name) # get release group release_group = parse_result.release_group # get version version = parse_result.version logger.log(u"Added RSS item: [" + name + "] to cache: [" + self.providerID + "]", logger.DEBUG) return [ "INSERT OR IGNORE INTO [" + self.providerID + "] (name, season, episodes, indexerid, url, time, quality, release_group, version) VALUES (?,?,?,?,?,?,?,?,?)", [name, season, episodeText, parse_result.show.indexerid, url, curTimestamp, quality, release_group, version]]
def update_scene_exceptions(indexer_id, scene_exceptions, season=-1): """ Given a indexer_id, and a list of all show scene exceptions, update the db. """ global exceptionsCache myDB = db.DBConnection('cache.db') myDB.action('DELETE FROM scene_exceptions WHERE indexer_id=? and season=?', [indexer_id, season]) logger.log(u"Updating scene exceptions", logger.MESSAGE) # A change has been made to the scene exception list. Let's clear the cache, to make this visible if indexer_id in exceptionsCache: exceptionsCache[indexer_id] = {} exceptionsCache[indexer_id][season] = scene_exceptions for cur_exception in scene_exceptions: cur_exception = toUnicode(cur_exception) myDB.action("INSERT INTO scene_exceptions (indexer_id, show_name, season) VALUES (?,?,?)", [indexer_id, cur_exception, season])
logger.log(u"Error while trying to retrieve XBMC API version for " + host + ": " + ex(e), logger.WARNING) return False # parse the json result try: result = json.load(response) response.close() logger.log(u"XBMC JSON response: " + str(result), logger.DEBUG) return result # need to return response for parsing except ValueError, e: logger.log(u"Unable to decode JSON: " + response, logger.WARNING) return False except IOError, e: logger.log(u"Warning: Couldn't contact XBMC JSON API at " + toUnicode(url) + " " + ex(e), logger.WARNING) return False def _update_library_json(self, host=None, showName=None): """Handles updating XBMC host via HTTP JSON-RPC Attempts to update the XBMC video library for a specific tv show if passed, otherwise update the whole library if enabled. Args: host: XBMC webserver host:port showName: Name of a TV show to specifically target the library update for Returns: Returns True or False
def _send_to_xbmc_json(self, command, host=None, username=None, password=None): """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 """ # fill in omitted parameters if not username: username = sickbeard.XBMC_USERNAME if not password: password = sickbeard.XBMC_PASSWORD if not host: logger.log(u'No XBMC host passed, aborting update', logger.DEBUG) return False command = command.encode('utf-8') logger.log(u"XBMC JSON command: " + command, logger.DEBUG) url = 'http://%s/jsonrpc' % (host) try: req = urllib2.Request(url, command) req.add_header("Content-type", "application/json") # if we have a password, use authentication if password: base64string = base64.encodestring('%s:%s' % (username, password))[:-1] authheader = "Basic %s" % base64string req.add_header("Authorization", authheader) logger.log( u"Contacting XBMC (with auth header) via url: " + toUnicode(url), logger.DEBUG) else: logger.log(u"Contacting XBMC via url: " + toUnicode(url), logger.DEBUG) try: response = urllib2.urlopen(req) except urllib2.URLError, e: logger.log( u"Error while trying to retrieve XBMC API version for " + host + ": " + ex(e), logger.WARNING) return False # parse the json result try: result = json.load(response) response.close() logger.log(u"XBMC JSON response: " + str(result), logger.DEBUG) return result # need to return response for parsing except ValueError, e: logger.log(u"Unable to decode JSON: " + response, logger.WARNING) return False
def retrieve_exceptions(): """ Looks up the exceptions on github, parses them into a dict, and inserts them into the scene_exceptions table in cache.db. Also clears the scene name cache. """ global exception_dict, anidb_exception_dict, xem_exception_dict # exceptions are stored on github pages for indexer in sickbeard.indexerApi().indexers: if shouldRefresh(sickbeard.indexerApi(indexer).name): logger.log(u"Checking for scene exception updates for " + sickbeard.indexerApi(indexer).name + "") url = sickbeard.indexerApi(indexer).config['scene_url'] url_data = helpers.getURL(url) if url_data is None: # When urlData is None, trouble connecting to github logger.log(u"Check scene exceptions update failed. Unable to get URL: " + url, logger.ERROR) continue else: setLastRefresh(sickbeard.indexerApi(indexer).name) # each exception is on one line with the format indexer_id: 'show name 1', 'show name 2', etc for cur_line in url_data.splitlines(): cur_line = cur_line.decode('utf-8') indexer_id, sep, aliases = cur_line.partition(':') # @UnusedVariable if not aliases: continue indexer_id = int(indexer_id) # regex out the list of shows, taking \' into account # alias_list = [re.sub(r'\\(.)', r'\1', x) for x in re.findall(r"'(.*?)(?<!\\)',?", aliases)] alias_list = [{re.sub(r'\\(.)', r'\1', x): -1} for x in re.findall(r"'(.*?)(?<!\\)',?", aliases)] exception_dict[indexer_id] = alias_list del alias_list del url_data # XEM scene exceptions _xem_exceptions_fetcher() 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] # AniDB scene exceptions _anidb_exceptions_fetcher() for anidb_ex in anidb_exception_dict: if anidb_ex in exception_dict: exception_dict[anidb_ex] = exception_dict[anidb_ex] + anidb_exception_dict[anidb_ex] else: exception_dict[anidb_ex] = anidb_exception_dict[anidb_ex] changed_exceptions = False # write all the exceptions we got off the net into the database myDB = db.DBConnection('cache.db') for cur_indexer_id in exception_dict: # get a list of the existing exceptions for this ID existing_exceptions = [x["show_name"] for x in myDB.select("SELECT * FROM scene_exceptions WHERE indexer_id = ?", [cur_indexer_id])] if not cur_indexer_id in exception_dict: continue for cur_exception_dict in exception_dict[cur_indexer_id]: cur_exception, curSeason = cur_exception_dict.items()[0] # if this exception isn't already in the DB then add it if cur_exception not in existing_exceptions: cur_exception = toUnicode(cur_exception) myDB.action("INSERT INTO scene_exceptions (indexer_id, show_name, season) VALUES (?,?,?)", [cur_indexer_id, cur_exception, curSeason]) changed_exceptions = True # since this could invalidate the results of the cache we clear it out after updating if changed_exceptions: logger.log(u"Updated scene exceptions") else: logger.log(u"No scene exceptions update needed") # cleanup exception_dict.clear() anidb_exception_dict.clear() xem_exception_dict.clear()
# parse the json result try: result = json.load(response) response.close() logger.log(u"XBMC JSON response: " + str(result), logger.DEBUG) return result # need to return response for parsing except ValueError, e: logger.log(u"Unable to decode JSON: " + response, logger.WARNING) return False except IOError, e: logger.log( u"Warning: Couldn't contact XBMC JSON API at " + toUnicode(url) + " " + ex(e), logger.WARNING) return False def _update_library_json(self, host=None, showName=None): """Handles updating XBMC host via HTTP JSON-RPC Attempts to update the XBMC video library for a specific tv show if passed, otherwise update the whole library if enabled. Args: host: XBMC webserver host:port showName: Name of a TV show to specifically target the library update for Returns: Returns True or False
def _addCacheEntry(self, name, url, parse_result=None, indexer_id=0): # check if we passed in a parsed result or should we try and create one if not parse_result: # create showObj from indexer_id if available showObj = None if indexer_id: showObj = helpers.findCertainShow(sickbeard.showList, indexer_id) try: myParser = NameParser(showObj=showObj, convert=True) parse_result = myParser.parse(name) except InvalidNameException: logger.log( u"Unable to parse the filename " + name + " into a valid episode", logger.DEBUG) return None except InvalidShowException: logger.log( u"Unable to parse the filename " + name + " into a valid show", logger.DEBUG) return None if not parse_result or not parse_result.series_name: return None # if we made it this far then lets add the parsed result to cache for usager later on season = parse_result.season_number if parse_result.season_number else 1 episodes = parse_result.episode_numbers if season and episodes: # store episodes as a seperated string episodeText = "|" + "|".join(map(str, episodes)) + "|" # get the current timestamp curTimestamp = int( time.mktime(datetime.datetime.today().timetuple())) # get quality of release quality = parse_result.quality name = toUnicode(name) # get release group release_group = parse_result.release_group # get version version = parse_result.version logger.log( u"Added RSS item: [" + name + "] to cache: [" + self.providerID + "]", logger.DEBUG) return [ "INSERT OR IGNORE INTO [" + self.providerID + "] (name, season, episodes, indexerid, url, time, quality, release_group, version) VALUES (?,?,?,?,?,?,?,?,?)", [ name, season, episodeText, parse_result.show.indexerid, url, curTimestamp, quality, release_group, version ] ]