SickGear/sickbeard/sab.py

224 lines
7.3 KiB
Python

# Author: Nic Wolfe <nic@wolfeden.ca>
# URL: http://code.google.com/p/sickbeard/
#
# This file is part of SickGear.
#
# SickGear is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# SickGear is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with SickGear. If not, see <http://www.gnu.org/licenses/>.
from six import moves
import urllib
import sickbeard
from lib import MultipartPostHandler
import urllib2
try:
import json
except ImportError:
from lib import simplejson as json
from sickbeard.common import USER_AGENT
from sickbeard import logger
from sickbeard.exceptions import ex
def sendNZB(nzb):
"""
Sends an NZB to SABnzbd via the API.
nzb: The NZBSearchResult object to send to SAB
"""
# set up a dict with the URL params in it
params = {}
if sickbeard.SAB_USERNAME is not None:
params['ma_username'] = sickbeard.SAB_USERNAME
if sickbeard.SAB_PASSWORD is not None:
params['ma_password'] = sickbeard.SAB_PASSWORD
if sickbeard.SAB_APIKEY is not None:
params['apikey'] = sickbeard.SAB_APIKEY
if sickbeard.SAB_CATEGORY is not None:
params['cat'] = sickbeard.SAB_CATEGORY
# use high priority if specified (recently aired episode)
if nzb.priority == 1:
params['priority'] = 1
# if it's a normal result we just pass SAB the URL
if nzb.resultType == "nzb":
params['mode'] = 'addurl'
params['name'] = nzb.url
# if we get a raw data result we want to upload it to SAB
elif nzb.resultType == "nzbdata":
params['mode'] = 'addfile'
multiPartParams = {"nzbfile": (nzb.name + ".nzb", nzb.extraInfo[0])}
url = sickbeard.SAB_HOST + "api?" + urllib.urlencode(params)
logger.log(u"Sending NZB to SABnzbd: %s" % nzb.name)
logger.log(u"SABnzbd URL: " + url, logger.DEBUG)
try:
# if we have the URL to an NZB then we've built up the SAB API URL already so just call it
if nzb.resultType == "nzb":
f = urllib.urlopen(url)
# if we are uploading the NZB data to SAB then we need to build a little POST form and send it
elif nzb.resultType == "nzbdata":
cookies = moves.http_cookiejar.CookieJar()
opener = urllib2.build_opener(urllib2.HTTPCookieProcessor(cookies),
MultipartPostHandler.MultipartPostHandler)
req = urllib2.Request(url,
multiPartParams,
headers={'User-Agent': USER_AGENT})
f = opener.open(req)
except (EOFError, IOError) as e:
logger.log(u"Unable to connect to SABnzbd: " + ex(e), logger.ERROR)
return False
except moves.http_client.InvalidURL as e:
logger.log(u"Invalid SABnzbd host, check your config: " + ex(e), logger.ERROR)
return False
# this means we couldn't open the connection or something just as bad
if f is None:
logger.log(u"No data returned from SABnzbd, NZB not sent", logger.ERROR)
return False
# if we opened the URL connection then read the result from SAB
try:
result = f.readlines()
except Exception as e:
logger.log(u"Error trying to get result from SABnzbd, NZB not sent: " + ex(e), logger.ERROR)
return False
# SAB shouldn't return a blank result, this most likely (but not always) means that it timed out and didn't recieve the NZB
if len(result) == 0:
logger.log(u"No data returned from SABnzbd, NZB not sent", logger.ERROR)
return False
# massage the result a little bit
sabText = result[0].strip()
logger.log(u"Result text from SABnzbd: " + sabText, logger.DEBUG)
# do some crude parsing of the result text to determine what SAB said
if sabText == "ok":
logger.log(u"NZB sent to SABnzbd successfully", logger.DEBUG)
return True
elif sabText == "Missing authentication":
logger.log(u"Incorrect username/password sent to SABnzbd, NZB not sent", logger.ERROR)
return False
else:
logger.log(u"Unknown failure sending NZB to SABnzbd. Return text is: " + sabText, logger.ERROR)
return False
def _checkSabResponse(f):
try:
result = f.readlines()
except Exception as e:
logger.log(u"Error trying to get result from SABnzbd" + ex(e), logger.ERROR)
return False, "Error from SABnzbd"
if len(result) == 0:
logger.log(u"No data returned from SABnzbd, NZB not sent", logger.ERROR)
return False, "No data from SABnzbd"
sabText = result[0].strip()
sabJson = {}
try:
sabJson = json.loads(sabText)
except ValueError as e:
pass
if sabText == "Missing authentication":
logger.log(u"Incorrect username/password sent to SABnzbd", logger.ERROR)
return False, "Incorrect username/password sent to SABnzbd"
elif 'error' in sabJson:
logger.log(sabJson['error'], logger.ERROR)
return False, sabJson['error']
else:
return True, sabText
def _sabURLOpenSimple(url):
try:
f = urllib.urlopen(url)
except (EOFError, IOError) as e:
logger.log(u"Unable to connect to SABnzbd: " + ex(e), logger.ERROR)
return False, "Unable to connect"
except moves.http_client.InvalidURL as e:
logger.log(u"Invalid SABnzbd host, check your config: " + ex(e), logger.ERROR)
return False, "Invalid SABnzbd host"
if f is None:
logger.log(u"No data returned from SABnzbd", logger.ERROR)
return False, "No data returned from SABnzbd"
else:
return True, f
def getSabAccesMethod(host=None, username=None, password=None, apikey=None):
url = host + "api?mode=auth"
result, f = _sabURLOpenSimple(url)
if not result:
return False, f
result, sabText = _checkSabResponse(f)
if not result:
return False, sabText
return True, sabText
def testAuthentication(host=None, username=None, password=None, apikey=None):
"""
Sends a simple API request to SAB to determine if the given connection information is connect
host: The host where SAB is running (incl port)
username: The username to use for the HTTP request
password: The password to use for the HTTP request
apikey: The API key to provide to SAB
Returns: A tuple containing the success boolean and a message
"""
# build up the URL parameters
params = {}
params['mode'] = 'queue'
params['output'] = 'json'
params['ma_username'] = username
params['ma_password'] = password
params['apikey'] = apikey
url = host + "api?" + urllib.urlencode(params)
# send the test request
logger.log(u"SABnzbd test URL: " + url, logger.DEBUG)
result, f = _sabURLOpenSimple(url)
if not result:
return False, f
# check the result and determine if it's good or not
result, sabText = _checkSabResponse(f)
if not result:
return False, sabText
return True, "Success"