from base64 import standard_b64encode from datetime import timedelta import re import shutil import socket import traceback import xmlrpclib from couchpotato.core.downloaders.base import Downloader, ReleaseDownloadList from couchpotato.core.helpers.encoding import ss, sp from couchpotato.core.helpers.variable import tryInt, md5, cleanHost from couchpotato.core.logger import CPLog log = CPLog(__name__) autoload = 'NZBGet' class NZBGet(Downloader): protocol = ['nzb'] rpc = 'xmlrpc' def download(self, data = None, media = None, filedata = None): if not media: media = {} if not data: data = {} if not filedata: log.error('Unable to get NZB file: %s', traceback.format_exc()) return False log.info('Sending "%s" to NZBGet.', data.get('name')) nzb_name = ss('%s.nzb' % self.createNzbName(data, media)) rpc = self.getRPC() try: if rpc.writelog('INFO', 'CouchPotato connected to drop off %s.' % nzb_name): log.debug('Successfully connected to NZBGet') else: log.info('Successfully connected to NZBGet, but unable to send a message') except socket.error: log.error('NZBGet is not responding. Please ensure that NZBGet is running and host setting is correct.') return False except xmlrpclib.ProtocolError as e: if e.errcode == 401: log.error('Password is incorrect.') else: log.error('Protocol Error: %s', e) return False if re.search(r"^0", rpc.version()): xml_response = rpc.append(nzb_name, self.conf('category'), False, standard_b64encode(filedata.strip())) else: xml_response = rpc.append(nzb_name, self.conf('category'), tryInt(self.conf('priority')), False, standard_b64encode(filedata.strip())) if xml_response: log.info('NZB sent successfully to NZBGet') nzb_id = md5(data['url']) # about as unique as they come ;) couchpotato_id = "couchpotato=" + nzb_id groups = rpc.listgroups() file_id = [item['LastID'] for item in groups if item['NZBFilename'] == nzb_name] confirmed = rpc.editqueue("GroupSetParameter", 0, couchpotato_id, file_id) if confirmed: log.debug('couchpotato parameter set in nzbget download') return self.downloadReturnId(nzb_id) else: log.error('NZBGet could not add %s to the queue.', nzb_name) return False def test(self): rpc = self.getRPC() try: if rpc.writelog('INFO', 'CouchPotato connected to test connection'): log.debug('Successfully connected to NZBGet') else: log.info('Successfully connected to NZBGet, but unable to send a message') except socket.error: log.error('NZBGet is not responding. Please ensure that NZBGet is running and host setting is correct.') return False except xmlrpclib.ProtocolError as e: if e.errcode == 401: log.error('Password is incorrect.') else: log.error('Protocol Error: %s', e) return False return True def getAllDownloadStatus(self, ids): log.debug('Checking NZBGet download status.') rpc = self.getRPC() try: if rpc.writelog('INFO', 'CouchPotato connected to check status'): log.debug('Successfully connected to NZBGet') else: log.info('Successfully connected to NZBGet, but unable to send a message') except socket.error: log.error('NZBGet is not responding. Please ensure that NZBGet is running and host setting is correct.') return [] except xmlrpclib.ProtocolError as e: if e.errcode == 401: log.error('Password is incorrect.') else: log.error('Protocol Error: %s', e) return [] # Get NZBGet data try: status = rpc.status() groups = rpc.listgroups() queue = rpc.postqueue(0) history = rpc.history() except: log.error('Failed getting data: %s', traceback.format_exc(1)) return [] release_downloads = ReleaseDownloadList(self) for nzb in groups: try: nzb_id = [param['Value'] for param in nzb['Parameters'] if param['Name'] == 'couchpotato'][0] except: nzb_id = nzb['NZBID'] if nzb_id in ids: log.debug('Found %s in NZBGet download queue', nzb['NZBFilename']) timeleft = -1 try: if nzb['ActiveDownloads'] > 0 and nzb['DownloadRate'] > 0 and not (status['DownloadPaused'] or status['Download2Paused']): timeleft = str(timedelta(seconds = nzb['RemainingSizeMB'] / status['DownloadRate'] * 2 ^ 20)) except: pass release_downloads.append({ 'id': nzb_id, 'name': nzb['NZBFilename'], 'original_status': 'DOWNLOADING' if nzb['ActiveDownloads'] > 0 else 'QUEUED', # Seems to have no native API function for time left. This will return the time left after NZBGet started downloading this item 'timeleft': timeleft, }) for nzb in queue: # 'Parameters' is not passed in rpc.postqueue if nzb['NZBID'] in ids: log.debug('Found %s in NZBGet postprocessing queue', nzb['NZBFilename']) release_downloads.append({ 'id': nzb['NZBID'], 'name': nzb['NZBFilename'], 'original_status': nzb['Stage'], 'timeleft': str(timedelta(seconds = 0)) if not status['PostPaused'] else -1, }) for nzb in history: try: nzb_id = [param['Value'] for param in nzb['Parameters'] if param['Name'] == 'couchpotato'][0] except: nzb_id = nzb['NZBID'] if nzb_id in ids: log.debug('Found %s in NZBGet history. ParStatus: %s, ScriptStatus: %s, Log: %s', (nzb['NZBFilename'] , nzb['ParStatus'], nzb['ScriptStatus'] , nzb['Log'])) release_downloads.append({ 'id': nzb_id, 'name': nzb['NZBFilename'], 'status': 'completed' if nzb['ParStatus'] in ['SUCCESS', 'NONE'] and nzb['ScriptStatus'] in ['SUCCESS', 'NONE'] else 'failed', 'original_status': nzb['ParStatus'] + ', ' + nzb['ScriptStatus'], 'timeleft': str(timedelta(seconds = 0)), 'folder': sp(nzb['DestDir']) }) return release_downloads def removeFailed(self, release_download): log.info('%s failed downloading, deleting...', release_download['name']) rpc = self.getRPC() try: if rpc.writelog('INFO', 'CouchPotato connected to delete some history'): log.debug('Successfully connected to NZBGet') else: log.info('Successfully connected to NZBGet, but unable to send a message') except socket.error: log.error('NZBGet is not responding. Please ensure that NZBGet is running and host setting is correct.') return False except xmlrpclib.ProtocolError as e: if e.errcode == 401: log.error('Password is incorrect.') else: log.error('Protocol Error: %s', e) return False try: history = rpc.history() nzb_id = None path = None for hist in history: for param in hist['Parameters']: if param['Name'] == 'couchpotato' and param['Value'] == release_download['id']: nzb_id = hist['ID'] path = hist['DestDir'] if nzb_id and path and rpc.editqueue('HistoryDelete', 0, "", [tryInt(nzb_id)]): shutil.rmtree(path, True) except: log.error('Failed deleting: %s', traceback.format_exc(0)) return False return True def getRPC(self): url = cleanHost(host = self.conf('host'), ssl = self.conf('ssl'), username = self.conf('username'), password = self.conf('password')) + self.rpc return xmlrpclib.ServerProxy(url) config = [{ 'name': 'nzbget', 'groups': [ { 'tab': 'downloaders', 'list': 'download_providers', 'name': 'nzbget', 'label': 'NZBGet', 'description': 'Use NZBGet to download NZBs.', 'wizard': True, 'options': [ { 'name': 'enabled', 'default': 0, 'type': 'enabler', 'radio_group': 'nzb', }, { 'name': 'host', 'default': 'localhost:6789', 'description': 'Hostname with port. Usually localhost:6789', }, { 'name': 'ssl', 'default': 0, 'type': 'bool', 'advanced': True, 'description': 'Use HyperText Transfer Protocol Secure, or https', }, { 'name': 'username', 'default': 'nzbget', 'advanced': True, 'description': 'Set a different username to connect. Default: nzbget', }, { 'name': 'password', 'type': 'password', 'description': 'Default NZBGet password is tegbzn6789', }, { 'name': 'category', 'default': 'Movies', 'description': 'The category CP places the nzb in. Like movies or couchpotato', }, { 'name': 'priority', 'advanced': True, 'default': '0', 'type': 'dropdown', 'values': [('Very Low', -100), ('Low', -50), ('Normal', 0), ('High', 50), ('Very High', 100)], 'description': 'Only change this if you are using NZBget 9.0 or higher', }, { 'name': 'manual', 'default': 0, 'type': 'bool', 'advanced': True, 'description': 'Disable this downloader for automated searches, but use it when I manually send a release.', }, { 'name': 'delete_failed', 'default': True, 'advanced': True, 'type': 'bool', 'description': 'Delete a release after the download has failed.', }, ], } ], }]