diff --git a/data/interfaces/default/config.html b/data/interfaces/default/config.html index 1ded606c..ed2c2e66 100755 --- a/data/interfaces/default/config.html +++ b/data/interfaces/default/config.html @@ -185,6 +185,34 @@

Usenet

+ +
+ + +
+
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+
@@ -698,6 +726,25 @@

NotifyMyAndroid

} }); + if ($("#nzb_downloader_nzbget").is(":checked")) + { + $("#nzbget_options").show(); + } + else + { + $("#nzbget_options").hide(); + } + $("#nzb_downloader_nzbget").click(function(){ + if ($("#nzb_downloader_nzbget").is(":checked")) + { + $("#nzbget_options").slideDown(); + } + else + { + $("#nzbget_options").slideUp(); + } + }); + if ($("#twitter").is(":checked")) { $("#twitteroptions").show(); diff --git a/lazylibrarian/__init__.py b/lazylibrarian/__init__.py index f44556a0..0b7ba2c1 100755 --- a/lazylibrarian/__init__.py +++ b/lazylibrarian/__init__.py @@ -67,6 +67,12 @@ SAB_API = None SAB_CAT = None +NZBGET_HOST = None +NZBGET_USER = None +NZBGET_PASS = None +NZBGET_CATEGORY = None +NZBGET_PRIORITY = None + DESTINATION_COPY = False DESTINATION_DIR = None DOWNLOAD_DIR = None @@ -117,6 +123,7 @@ USE_TOR = False NZB_DOWNLOADER_SABNZBD = False +NZB_DOWNLOADER_NZBGET = False NZB_DOWNLOADER_BLACKHOLE = False NZB_BLACKHOLEDIR = None USENET_RETENTION = None @@ -247,6 +254,7 @@ def initialize(): global __INITIALIZED__, FULL_PATH, PROG_DIR, LOGLEVEL, DAEMON, DATADIR, CONFIGFILE, CFG, LOGDIR, HTTP_HOST, HTTP_PORT, HTTP_USER, HTTP_PASS, HTTP_ROOT, HTTP_LOOK, LAUNCH_BROWSER, LOGDIR, CACHEDIR, MATCH_RATIO, \ PROXY_HOST, PROXY_TYPE, \ IMP_ONLYISBN, IMP_PREFLANG, IMP_AUTOADD, SAB_HOST, SAB_PORT, SAB_SUBDIR, SAB_API, SAB_USER, SAB_PASS, DESTINATION_DIR, DESTINATION_COPY, DOWNLOAD_DIR, SAB_CAT, USENET_RETENTION, NZB_BLACKHOLEDIR, GR_API, GB_API, BOOK_API, \ + NZBGET_HOST, NZBGET_USER, NZBGET_PASS, NZBGET_CATEGORY, NZBGET_PRIORITY, NZB_DOWNLOADER_NZBGET, \ NZBMATRIX, NZBMATRIX_USER, NZBMATRIX_API, NEWZNAB, NEWZNAB_HOST, NEWZNAB_API, NEWZBIN, NEWZBIN_UID, NEWZBIN_PASS, NEWZNAB2, NEWZNAB_HOST2, NEWZNAB_API2, EBOOK_TYPE, KAT, USENETCRAWLER, USENETCRAWLER_HOST, USENETCRAWLER_API, \ VERSIONCHECK_INTERVAL, SEARCH_INTERVAL, SCAN_INTERVAL, EBOOK_DEST_FOLDER, EBOOK_DEST_FILE, MAG_DEST_FOLDER, MAG_DEST_FILE, USE_TWITTER, TWITTER_NOTIFY_ONSNATCH, TWITTER_NOTIFY_ONDOWNLOAD, TWITTER_USERNAME, TWITTER_PASSWORD, TWITTER_PREFIX, \ USE_BOXCAR, BOXCAR_NOTIFY_ONSNATCH, BOXCAR_NOTIFY_ONDOWNLOAD, BOXCAR_TOKEN, TORRENT_DIR, TOR_DOWNLOADER_BLACKHOLE, TOR_DOWNLOADER_UTORRENT, USE_TOR, USE_NZB, NZB_DOWNLOADER_SABNZBD, NZB_DOWNLOADER_BLACKHOLE, \ @@ -333,6 +341,11 @@ def initialize(): SAB_API = check_setting_str(CFG, 'SABnzbd', 'sab_api', '') SAB_CAT = check_setting_str(CFG, 'SABnzbd', 'sab_cat', '') + NZBGET_HOST = check_setting_str(CFG, 'NZBGet', 'nzbget_host', '') + NZBGET_USER = check_setting_str(CFG, 'NZBGet', 'nzbget_user', '') + NZBGET_PASS = check_setting_str(CFG, 'NZBGet', 'nzbget_pass', '') + NZBGET_CATEGORY = check_setting_str(CFG, 'NZBGet', 'nzbget_cat', '') + NZBGET_PRIORITY = check_setting_str(CFG, 'NZBGet', 'nzbget_priority', '') DESTINATION_COPY = bool(check_setting_int(CFG, 'General', 'destination_copy', 0)) DESTINATION_DIR = check_setting_str(CFG, 'General','destination_dir', '') @@ -342,6 +355,7 @@ def initialize(): USE_TOR = bool(check_setting_int(CFG, 'DLMethod', 'use_tor', 0)) NZB_DOWNLOADER_SABNZBD = bool(check_setting_int(CFG, 'USENET', 'nzb_downloader_sabnzbd', 0)) + NZB_DOWNLOADER_NZBGET = bool(check_setting_int(CFG, 'USENET', 'nzb_downloader_nzbget', 0)) NZB_DOWNLOADER_BLACKHOLE = bool(check_setting_int(CFG, 'USENET', 'nzb_downloader_blackhole', 0)) NZB_BLACKHOLEDIR = check_setting_str(CFG, 'USENET', 'nzb_blackholedir', '') USENET_RETENTION = check_setting_str(CFG, 'USENET', 'usenet_retention', '') @@ -525,6 +539,7 @@ def config_write(): new_config['USENET'] = {} new_config['USENET']['nzb_downloader_sabnzbd'] = NZB_DOWNLOADER_SABNZBD + new_config['USENET']['nzb_downloader_nzbget'] = NZB_DOWNLOADER_NZBGET new_config['USENET']['nzb_downloader_blackhole'] = NZB_DOWNLOADER_BLACKHOLE new_config['USENET']['nzb_blackholedir'] = NZB_BLACKHOLEDIR new_config['USENET']['usenet_retention'] = USENET_RETENTION @@ -538,6 +553,13 @@ def config_write(): new_config['SABnzbd']['sab_api'] = SAB_API new_config['SABnzbd']['sab_cat'] = SAB_CAT + new_config['NZBGet'] = {} + new_config['NZBGet']['nzbget_host'] = NZBGET_HOST + new_config['NZBGet']['nzbget_user'] = NZBGET_USER + new_config['NZBGet']['nzbget_pass'] = NZBGET_PASS + new_config['NZBGet']['nzbget_cat'] = NZBGET_CATEGORY + new_config['NZBGet']['nzbget_priority'] = NZBGET_PRIORITY + new_config['General']['destination_dir'] = DESTINATION_DIR new_config['General']['destination_copy'] = int(DESTINATION_COPY) new_config['General']['download_dir'] = DOWNLOAD_DIR diff --git a/lazylibrarian/classes.py b/lazylibrarian/classes.py new file mode 100644 index 00000000..3ed101ae --- /dev/null +++ b/lazylibrarian/classes.py @@ -0,0 +1,136 @@ +# This file is part of LazyLibrarian. +# +# LazyLibrarian 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. +# +# LazyLibrarian 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 LazyLibrarian. If not, see . + +######################################### +## Stolen from Sick-Beard's classes.py ## +######################################### + + +import urllib + +from common import USER_AGENT + + +class LazyLibrarianURLopener(urllib.FancyURLopener): + version = USER_AGENT + + +class AuthURLOpener(LazyLibrarianURLopener): + """ + URLOpener class that supports http auth without needing interactive password entry. + If the provided username/password don't work it simply fails. + + user: username to use for HTTP auth + pw: password to use for HTTP auth + """ + + def __init__(self, user, pw): + self.username = user + self.password = pw + + # remember if we've tried the username/password before + self.numTries = 0 + + # call the base class + urllib.FancyURLopener.__init__(self) + + def prompt_user_passwd(self, host, realm): + """ + Override this function and instead of prompting just give the + username/password that were provided when the class was instantiated. + """ + + # if this is the first try then provide a username/password + if self.numTries == 0: + self.numTries = 1 + return (self.username, self.password) + + # if we've tried before then return blank which cancels the request + else: + return ('', '') + + # this is pretty much just a hack for convenience + def openit(self, url): + self.numTries = 0 + return LazyLibrarianURLopener.open(self, url) + + +class SearchResult: + """ + Represents a search result from an indexer. + """ + + def __init__(self): + self.provider = -1 + + # URL to the NZB/torrent file + self.url = "" + + # used by some providers to store extra info associated with the result + self.extraInfo = [] + + # quality of the release + self.quality = -1 + + # release name + self.name = "" + + def __str__(self): + + if self.provider is None: + return "Invalid provider, unable to print self" + + myString = self.provider.name + " @ " + self.url + "\n" + myString += "Extra Info:\n" + for extra in self.extraInfo: + myString += " " + extra + "\n" + return myString + + +class NZBSearchResult(SearchResult): + """ + Regular NZB result with an URL to the NZB + """ + resultType = "nzb" + + +class NZBDataSearchResult(SearchResult): + """ + NZB result where the actual NZB XML data is stored in the extraInfo + """ + resultType = "nzbdata" + + +class TorrentSearchResult(SearchResult): + """ + Torrent result with an URL to the torrent + """ + resultType = "torrent" + + +class Proper: + def __init__(self, name, url, date): + self.name = name + self.url = url + self.date = date + self.provider = None + self.quality = -1 + + self.tvdbid = -1 + self.season = -1 + self.episode = -1 + + def __str__(self): + return str(self.date) + " " + self.name + " " + str(self.season) + "x" + str(self.episode) + " of " + str(self.tvdbid) diff --git a/lazylibrarian/nzbget.py b/lazylibrarian/nzbget.py new file mode 100644 index 00000000..21420862 --- /dev/null +++ b/lazylibrarian/nzbget.py @@ -0,0 +1,126 @@ +# This file is modified to work with lazylibrarian by CurlyMo as a part of XBian - XBMC on the Raspberry Pi + +# Author: Nic Wolfe +# URL: http://code.google.com/p/sickbeard/ +# +# This file is part of LazyLibrarian. +# +# LazyLibrarian 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. +# +# LazyLibrarian 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 LazyLibrarian. If not, see . + + +import httplib + +import lazylibrarian + +from base64 import standard_b64encode +import xmlrpclib + +from lazylibrarian import logger + + +def sendNZB(nzb): + + addToTop = False + nzbgetXMLrpc = "%(username)s:%(password)s@%(host)s/xmlrpc" + + if lazylibrarian.NZBGET_HOST is None: + logger.error(u"No NZBget host found in configuration. Please configure it.") + return False + + if lazylibrarian.NZBGET_HOST.startswith('https://'): + nzbgetXMLrpc = 'https://' + nzbgetXMLrpc + lazylibrarian.NZBGET_HOST.replace('https://', '', 1) + else: + nzbgetXMLrpc = 'http://' + nzbgetXMLrpc + lazylibrarian.NZBGET_HOST.replace('http://', '', 1) + + url = nzbgetXMLrpc % {"host": lazylibrarian.NZBGET_HOST, "username": lazylibrarian.NZBGET_USER, "password": lazylibrarian.NZBGET_PASS} + + nzbGetRPC = xmlrpclib.ServerProxy(url) + try: + if nzbGetRPC.writelog("INFO", "lazylibrarian connected to drop of %s any moment now." % (nzb.name + ".nzb")): + logger.debug(u"Successfully connected to NZBget") + else: + logger.info(u"Successfully connected to NZBget, but unable to send a message" % (nzb.name + ".nzb")) + + except httplib.socket.error, e: + logger.error(u"Please check your NZBget host and port (if it is running). NZBget is not responding to this combination") + return False + + except xmlrpclib.ProtocolError, e: + if e.errmsg == "Unauthorized": + logger.error(u"NZBget password is incorrect.") + else: + logger.error(u"Protocol Error: " + e.errmsg) + return False + + nzbcontent64 = None + if nzb.resultType == "nzbdata": + data = nzb.extraInfo[0] + nzbcontent64 = standard_b64encode(data) + + logger.info(u"Sending NZB to NZBget") + logger.debug(u"URL: " + url) + + dupekey = "" + dupescore = 0 + + try: + # Find out if nzbget supports priority (Version 9.0+), old versions beginning with a 0.x will use the old command + nzbget_version_str = nzbGetRPC.version() + nzbget_version = int(nzbget_version_str[:nzbget_version_str.find(".")]) + if nzbget_version == 0: + if nzbcontent64 is not None: + nzbget_result = nzbGetRPC.append(nzb.name + ".nzb", lazylibrarian.NZBGET_CATEGORY, addToTop, nzbcontent64) + else: + # from lazylibrarian.common.providers.generic import GenericProvider + # if nzb.resultType == "nzb": + # genProvider = GenericProvider("") + # data = genProvider.getURL(nzb.url) + # if (data is None): + # return False + # nzbcontent64 = standard_b64encode(data) + # nzbget_result = nzbGetRPC.append(nzb.name + ".nzb", lazylibrarian.NZBGET_CATEGORY, addToTop, nzbcontent64) + return False + elif nzbget_version == 12: + if nzbcontent64 is not None: + nzbget_result = nzbGetRPC.append(nzb.name + ".nzb", lazylibrarian.NZBGET_CATEGORY, lazylibrarian.NZBGET_PRIORITY, False, + nzbcontent64, False, dupekey, dupescore, "score") + else: + nzbget_result = nzbGetRPC.appendurl(nzb.name + ".nzb", lazylibrarian.NZBGET_CATEGORY, lazylibrarian.NZBGET_PRIORITY, False, + nzb.url, False, dupekey, dupescore, "score") + # v13+ has a new 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.) + elif nzbget_version >= 13: + nzbget_result = True if nzbGetRPC.append(nzb.name + ".nzb", nzbcontent64 if nzbcontent64 is not None else nzb.url, + lazylibrarian.NZBGET_CATEGORY, lazylibrarian.NZBGET_PRIORITY, False, False, dupekey, dupescore, + "score") > 0 else False + else: + if nzbcontent64 is not None: + nzbget_result = nzbGetRPC.append(nzb.name + ".nzb", lazylibrarian.NZBGET_CATEGORY, lazylibrarian.NZBGET_PRIORITY, False, + nzbcontent64) + else: + nzbget_result = nzbGetRPC.appendurl(nzb.name + ".nzb", lazylibrarian.NZBGET_CATEGORY, lazylibrarian.NZBGET_PRIORITY, False, + nzb.url) + + if nzbget_result: + logger.debug(u"NZB sent to NZBget successfully") + return True + else: + logger.error(u"NZBget could not add %s to the queue" % (nzb.name + ".nzb")) + return False + except: + logger.error(u"Connect Error to NZBget: could not add %s to the queue" % (nzb.name + ".nzb")) + return False diff --git a/lazylibrarian/searchmag.py b/lazylibrarian/searchmag.py index ae0eeab5..1a037bac 100644 --- a/lazylibrarian/searchmag.py +++ b/lazylibrarian/searchmag.py @@ -40,7 +40,7 @@ def searchmagazines(mags=None): searchterm = re.sub('[\.\-\/]', ' ', searchterm).encode('utf-8') searchlist.append({"bookid": bookid, "searchterm": searchterm}) - if not lazylibrarian.SAB_HOST and not lazylibrarian.BLACKHOLE: + if not lazylibrarian.SAB_HOST and not lazylibrarian.NZB_DOWNLOADER_BLACKHOLE: logger.info('No download method is set, use SABnzbd or blackhole') if not lazylibrarian.NEWZNAB and not lazylibrarian.NEWZNAB2 and not lazylibrarian.USENETCRAWLER: diff --git a/lazylibrarian/searchnzb.py b/lazylibrarian/searchnzb.py index 640444db..12b63a87 100755 --- a/lazylibrarian/searchnzb.py +++ b/lazylibrarian/searchnzb.py @@ -5,7 +5,7 @@ import lazylibrarian -from lazylibrarian import logger, database, formatter, providers, sabnzbd, SimpleCache, notifiers, searchmag +from lazylibrarian import logger, database, formatter, providers, nzbget, sabnzbd, SimpleCache, notifiers, searchmag, classes import lib.fuzzywuzzy as fuzzywuzzy from lib.fuzzywuzzy import fuzz, process @@ -148,6 +148,25 @@ def DownloadMethod(bookid=None, nzbprov=None, nzbtitle=None, nzburl=None): if lazylibrarian.SAB_HOST and not lazylibrarian.NZB_DOWNLOADER_BLACKHOLE: download = sabnzbd.SABnzbd(nzbtitle, nzburl) + elif lazylibrarian.NZBGET_HOST and not lazylibrarian.NZB_DOWNLOADER_BLACKHOLE: + try: + req = urllib2.Request(nzburl) + if lazylibrarian.PROXY_HOST: + req.set_proxy(lazylibrarian.PROXY_HOST, lazylibrarian.PROXY_TYPE) + req.add_header('User-Agent', USER_AGENT) + nzbfile = urllib2.urlopen(req, timeout=90).read() + + except urllib2.URLError, e: + logger.warn('Error fetching nzb from url: ' + nzburl + ' %s' % e) + nzbfile = False; + if (nzbfile): + + nzb = classes.NZBDataSearchResult() + nzb.extraInfo.append(nzbfile) + nzb.resultType = "nzbdata" + nzb.name = nzbtitle + download = nzbget.sendNZB(nzb) + elif lazylibrarian.NZB_DOWNLOADER_BLACKHOLE: try: diff --git a/lazylibrarian/webServe.py b/lazylibrarian/webServe.py index 2f50e0b0..9a5bdd6a 100755 --- a/lazylibrarian/webServe.py +++ b/lazylibrarian/webServe.py @@ -87,6 +87,11 @@ def config(self): "sab_api": lazylibrarian.SAB_API, "sab_user": lazylibrarian.SAB_USER, "sab_pass": lazylibrarian.SAB_PASS, + "nzbget_host": lazylibrarian.NZBGET_HOST, + "nzbget_user": lazylibrarian.NZBGET_USER, + "nzbget_pass": lazylibrarian.NZBGET_PASS, + "nzbget_cat": lazylibrarian.NZBGET_CATEGORY, + "nzbget_priority": lazylibrarian.NZBGET_PRIORITY, "destination_copy": checked(lazylibrarian.DESTINATION_COPY), "destination_dir": lazylibrarian.DESTINATION_DIR, "download_dir": lazylibrarian.DOWNLOAD_DIR, @@ -142,6 +147,7 @@ def config(self): "use_nzb" : checked(lazylibrarian.USE_NZB), "use_tor" : checked(lazylibrarian.USE_TOR), "nzb_downloader_sabnzbd" : checked(lazylibrarian.NZB_DOWNLOADER_SABNZBD), + "nzb_downloader_nzbget" : checked(lazylibrarian.NZB_DOWNLOADER_NZBGET), "nzb_downloader_blackhole" : checked(lazylibrarian.NZB_DOWNLOADER_BLACKHOLE), "tor_downloader_utorrent" : checked(lazylibrarian.TOR_DOWNLOADER_UTORRENT), "tor_downloader_transmission" : checked(lazylibrarian.TOR_DOWNLOADER_TRANSMISSION), @@ -157,8 +163,9 @@ def config(self): return serve_template(templatename="config.html", title="Settings", config=config) config.exposed = True - def configUpdate(self, http_host='0.0.0.0', http_root=None, http_user=None, http_port=5299, http_pass=None, http_look=None, launch_browser=0, logdir=None, imp_onlyisbn=0, imp_preflang=None, imp_autoadd=None, match_ratio=80, nzb_downloader_sabnzbd=0, nzb_downloader_blackhole=0, use_nzb=0, use_tor=0, proxy_host=None, proxy_type=None, + def configUpdate(self, http_host='0.0.0.0', http_root=None, http_user=None, http_port=5299, http_pass=None, http_look=None, launch_browser=0, logdir=None, imp_onlyisbn=0, imp_preflang=None, imp_autoadd=None, match_ratio=80, nzb_downloader_sabnzbd=0, nzb_downloader_nzbget=0, nzb_downloader_blackhole=0, use_nzb=0, use_tor=0, proxy_host=None, proxy_type=None, sab_host=None, sab_port=None, sab_subdir=None, sab_api=None, sab_user=None, sab_pass=None, destination_copy=0, destination_dir=None, download_dir=None, sab_cat=None, usenet_retention=None, nzb_blackholedir=None, torrent_dir=None, numberofseeders=0, tor_downloader_blackhole=0, tor_downloader_utorrent=0, + nzbget_host=None, nzbget_user=None, nzbget_pass=None, nzbget_cat=None, nzbget_priority=None, newznab=0, newznab_host=None, newznab_api=None, newznab2=0, newznab_host2=None, newznab_api2=None,newzbin=0, newzbin_uid=None, newzbin_pass=None, kat=0, ebook_type=None, book_api=None, gr_api=None, gb_api=None, usenetcrawler = 0, usenetcrawler_host=None, usenetcrawler_api = None, versioncheck_interval=None, search_interval=None, scan_interval=None, ebook_dest_folder=None, ebook_dest_file=None, mag_dest_folder=None, mag_dest_file=None, use_twitter=0, twitter_notify_onsnatch=0, twitter_notify_ondownload=0, utorrent_host=None, utorrent_user=None, utorrent_pass=None, notfound_status='Wanted', full_scan=0, add_author=1, tor_downloader_transmission=0, transmission_host=None, transmission_user=None, transmission_pass=None, @@ -189,12 +196,19 @@ def configUpdate(self, http_host='0.0.0.0', http_root=None, http_user=None, http lazylibrarian.SAB_PASS = sab_pass lazylibrarian.SAB_CAT = sab_cat + lazylibrarian.NZBGET_HOST = nzbget_host + lazylibrarian.NZBGET_USER = nzbget_user + lazylibrarian.NZBGET_PASS = nzbget_pass + lazylibrarian.NZBGET_CATEGORY = nzbget_cat + lazylibrarian.NZBGET_PRIORITY = nzbget_priority + lazylibrarian.DESTINATION_COPY = destination_copy lazylibrarian.DESTINATION_DIR = destination_dir lazylibrarian.DOWNLOAD_DIR = download_dir lazylibrarian.USENET_RETENTION = usenet_retention lazylibrarian.NZB_BLACKHOLEDIR = nzb_blackholedir lazylibrarian.NZB_DOWNLOADER_SABNZBD = nzb_downloader_sabnzbd + lazylibrarian.NZB_DOWNLOADER_NZBGET = nzb_downloader_nzbget lazylibrarian.NZB_DOWNLOADER_BLACKHOLE = nzb_downloader_blackhole lazylibrarian.TORRENT_DIR = torrent_dir lazylibrarian.NUMBEROFSEEDERS = numberofseeders