diff --git a/couchpotato/core/_base/clientscript/main.py b/couchpotato/core/_base/clientscript/main.py index 248d2bc5..c1be7e73 100644 --- a/couchpotato/core/_base/clientscript/main.py +++ b/couchpotato/core/_base/clientscript/main.py @@ -49,6 +49,7 @@ class ClientScript(Plugin): 'scripts/page/settings.js', 'scripts/page/about.js', 'scripts/page/manage.js', + 'scripts/misc/downloaders.js', ], } diff --git a/couchpotato/core/downloaders/base.py b/couchpotato/core/downloaders/base.py index 71da65ee..3bcf1f31 100644 --- a/couchpotato/core/downloaders/base.py +++ b/couchpotato/core/downloaders/base.py @@ -1,4 +1,5 @@ from base64 import b32decode, b16encode +from couchpotato.api import addApiView from couchpotato.core.event import addEvent from couchpotato.core.helpers.variable import mergeDicts from couchpotato.core.logger import CPLog @@ -42,6 +43,7 @@ class Downloader(Provider): addEvent('download.remove_failed', self._removeFailed) addEvent('download.pause', self._pause) addEvent('download.process_complete', self._processComplete) + addApiView('download.%s.test' % self.getName().lower(), self._test) def getEnabledProtocol(self): for download_protocol in self.protocol: @@ -158,6 +160,15 @@ class Downloader(Provider): (d_manual and manual or d_manual is False) and \ (not data or self.isCorrectProtocol(data.get('protocol'))) + def _test(self): + t = self.test() + if isinstance(t, tuple): + return {'success': t[0], 'msg': t[1]} + return {'success': t} + + def test(self): + return False + def _pause(self, release_download, pause = True): if self.isDisabled(manual = True, data = {}): return diff --git a/couchpotato/core/downloaders/blackhole/main.py b/couchpotato/core/downloaders/blackhole/main.py index 8449d09b..9a018354 100644 --- a/couchpotato/core/downloaders/blackhole/main.py +++ b/couchpotato/core/downloaders/blackhole/main.py @@ -1,5 +1,6 @@ from __future__ import with_statement from couchpotato.core.downloaders.base import Downloader +from couchpotato.core.helpers.encoding import sp from couchpotato.core.logger import CPLog from couchpotato.environment import Env import os @@ -67,6 +68,20 @@ class Blackhole(Downloader): return False + def test(self): + directory = self.conf('directory') + if directory and os.path.isdir(directory): + + test_file = sp(os.path.join(directory, 'couchpotato_test.txt')) + + # Check if folder is writable + self.createFile(test_file, 'This is a test file') + if os.path.isfile(test_file): + os.remove(test_file) + return True + + return False + def getEnabledProtocol(self): if self.conf('use_for') == 'both': return super(Blackhole, self).getEnabledProtocol() diff --git a/couchpotato/core/downloaders/deluge/main.py b/couchpotato/core/downloaders/deluge/main.py index c5f80167..59300958 100644 --- a/couchpotato/core/downloaders/deluge/main.py +++ b/couchpotato/core/downloaders/deluge/main.py @@ -20,14 +20,14 @@ class Deluge(Downloader): log = CPLog(__name__) drpc = None - def connect(self): + def connect(self, reconnect = False): # Load host from config and split out port. host = cleanHost(self.conf('host'), protocol = False).split(':') if not isInt(host[1]): log.error('Config properties are not filled in correctly, port is missing.') return False - if not self.drpc: + if not self.drpc or reconnect: self.drpc = DelugeRPC(host[0], port = host[1], username = self.conf('username'), password = self.conf('password')) return self.drpc @@ -86,6 +86,11 @@ class Deluge(Downloader): log.info('Torrent sent to Deluge successfully.') return self.downloadReturnId(remote_torrent) + def test(self): + if self.connect(True) and self.drpc.test(): + return True + return False + def getAllDownloadStatus(self, ids): log.debug('Checking Deluge download status.') @@ -178,6 +183,13 @@ class DelugeRPC(object): self.client = DelugeClient() self.client.connect(self.host, int(self.port), self.username, self.password) + def test(self): + try: + self.connect() + except: + return False + return True + def add_torrent_magnet(self, torrent, options): torrent_id = False try: diff --git a/couchpotato/core/downloaders/nzbget/main.py b/couchpotato/core/downloaders/nzbget/main.py index a690572c..3dad8670 100644 --- a/couchpotato/core/downloaders/nzbget/main.py +++ b/couchpotato/core/downloaders/nzbget/main.py @@ -16,7 +16,6 @@ log = CPLog(__name__) class NZBGet(Downloader): protocol = ['nzb'] - rpc = 'xmlrpc' def download(self, data = None, media = None, filedata = None): @@ -31,8 +30,7 @@ class NZBGet(Downloader): nzb_name = ss('%s.nzb' % self.createNzbName(data, media)) - url = cleanHost(host = self.conf('host'), ssl = self.conf('ssl'), username = self.conf('username'), password = self.conf('password')) + self.rpc - rpc = xmlrpclib.ServerProxy(url) + rpc = self.getRPC() try: if rpc.writelog('INFO', 'CouchPotato connected to drop off %s.' % nzb_name): @@ -68,12 +66,31 @@ class NZBGet(Downloader): 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.') - url = cleanHost(host = self.conf('host'), ssl = self.conf('ssl'), username = self.conf('username'), password = self.conf('password')) + self.rpc - rpc = xmlrpclib.ServerProxy(url) + rpc = self.getRPC() try: if rpc.writelog('INFO', 'CouchPotato connected to check status'): @@ -158,8 +175,7 @@ class NZBGet(Downloader): log.info('%s failed downloading, deleting...', release_download['name']) - url = cleanHost(host = self.conf('host'), ssl = self.conf('ssl'), username = self.conf('username'), password = self.conf('password')) + self.rpc - rpc = xmlrpclib.ServerProxy(url) + rpc = self.getRPC() try: if rpc.writelog('INFO', 'CouchPotato connected to delete some history'): @@ -194,3 +210,7 @@ class NZBGet(Downloader): 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) diff --git a/couchpotato/core/downloaders/nzbvortex/main.py b/couchpotato/core/downloaders/nzbvortex/main.py index 236e624c..d1525c89 100644 --- a/couchpotato/core/downloaders/nzbvortex/main.py +++ b/couchpotato/core/downloaders/nzbvortex/main.py @@ -42,6 +42,14 @@ class NZBVortex(Downloader): log.error('Something went wrong sending the NZB file: %s', traceback.format_exc()) return False + def test(self): + try: + login_result = self.login() + except: + return False + + return login_result + def getAllDownloadStatus(self, ids): raw_statuses = self.call('nzb') diff --git a/couchpotato/core/downloaders/pneumatic/main.py b/couchpotato/core/downloaders/pneumatic/main.py index 6af22d2d..bc1f6d04 100644 --- a/couchpotato/core/downloaders/pneumatic/main.py +++ b/couchpotato/core/downloaders/pneumatic/main.py @@ -1,5 +1,6 @@ from __future__ import with_statement from couchpotato.core.downloaders.base import Downloader +from couchpotato.core.helpers.encoding import sp from couchpotato.core.logger import CPLog import os import traceback @@ -56,3 +57,17 @@ class Pneumatic(Downloader): log.info('Failed to download file %s: %s', (data.get('name'), traceback.format_exc())) return False return False + + def test(self): + directory = self.conf('directory') + if directory and os.path.isdir(directory): + + test_file = sp(os.path.join(directory, 'couchpotato_test.txt')) + + # Check if folder is writable + self.createFile(test_file, 'This is a test file') + if os.path.isfile(test_file): + os.remove(test_file) + return True + + return False diff --git a/couchpotato/core/downloaders/rtorrent/main.py b/couchpotato/core/downloaders/rtorrent/main.py index 8e21e7fc..c4cb0fd6 100755 --- a/couchpotato/core/downloaders/rtorrent/main.py +++ b/couchpotato/core/downloaders/rtorrent/main.py @@ -20,6 +20,7 @@ class rTorrent(Downloader): protocol = ['torrent', 'torrent_magnet'] rt = None + error_msg = '' # Migration url to host options def __init__(self): @@ -48,9 +49,9 @@ class rTorrent(Downloader): self.rt = None return True - def connect(self): + def connect(self, reconnect = False): # Already connected? - if self.rt is not None: + if not reconnect and self.rt is not None: return self.rt url = cleanHost(self.conf('host'), protocol = True, ssl = self.conf('ssl')) @@ -69,9 +70,25 @@ class rTorrent(Downloader): else: self.rt = RTorrent(url) + self.error_msg = '' + try: + self.rt._verify_conn() + except AssertionError as e: + self.error_msg = e.message + self.rt = None + return self.rt - def updateProviderGroup(self, name, data): + def test(self): + if self.connect(True): + return True + + if self.error_msg: + return False, 'Connection failed: ' + self.error_msg + + return False + + def _update_provider_group(self, name, data): if data.get('seed_time'): log.info('seeding time ignored, not supported') diff --git a/couchpotato/core/downloaders/sabnzbd/main.py b/couchpotato/core/downloaders/sabnzbd/main.py index 72c23708..ba58c090 100644 --- a/couchpotato/core/downloaders/sabnzbd/main.py +++ b/couchpotato/core/downloaders/sabnzbd/main.py @@ -64,6 +64,26 @@ class Sabnzbd(Downloader): log.error('Error getting data from SABNZBd: %s', sab_data) return False + def test(self): + try: + sab_data = self.call({ + 'mode': 'version', + }) + v = sab_data.split('.') + if int(v[0]) == 0 and int(v[1]) < 7: + return False, 'Your Sabnzbd client is too old, please update to newest version.' + + # the version check will work even with wrong api key, so we need the next check as well + sab_data = self.call({ + 'mode': 'qstatus', + }) + if not sab_data: + return False + except: + return False + + return True + def getAllDownloadStatus(self, ids): log.debug('Checking SABnzbd download status.') diff --git a/couchpotato/core/downloaders/synology/main.py b/couchpotato/core/downloaders/synology/main.py index f964f37f..7e5b6098 100644 --- a/couchpotato/core/downloaders/synology/main.py +++ b/couchpotato/core/downloaders/synology/main.py @@ -45,6 +45,16 @@ class Synology(Downloader): finally: return self.downloadReturnId('') if response else False + def test(self): + host = cleanHost(self.conf('host'), protocol = False).split(':') + try: + srpc = SynologyRPC(host[0], host[1], self.conf('username'), self.conf('password')) + test_result = srpc.test() + except: + return False + + return test_result + def getEnabledProtocol(self): if self.conf('use_for') == 'both': return super(Synology, self).getEnabledProtocol() @@ -147,3 +157,6 @@ class SynologyRPC(object): self._logout() return result + + def test(self): + return bool(self._login()) diff --git a/couchpotato/core/downloaders/transmission/main.py b/couchpotato/core/downloaders/transmission/main.py index 2daeab46..4c42bf0f 100644 --- a/couchpotato/core/downloaders/transmission/main.py +++ b/couchpotato/core/downloaders/transmission/main.py @@ -19,14 +19,14 @@ class Transmission(Downloader): log = CPLog(__name__) trpc = None - def connect(self): + def connect(self, reconnect = False): # Load host from config and split out port. host = cleanHost(self.conf('host'), protocol = False).split(':') if not isInt(host[1]): log.error('Config properties are not filled in correctly, port is missing.') return False - if not self.trpc: + if not self.trpc or reconnect: self.trpc = TransmissionRPC(host[0], port = host[1], rpc_url = self.conf('rpc_url').strip('/ '), username = self.conf('username'), password = self.conf('password')) return self.trpc @@ -83,6 +83,11 @@ class Transmission(Downloader): log.info('Torrent sent to Transmission successfully.') return self.downloadReturnId(remote_torrent['torrent-added']['hashString']) + def test(self): + if self.connect(True) and self.trpc.get_session(): + return True + return False + def getAllDownloadStatus(self, ids): log.debug('Checking Transmission download status.') diff --git a/couchpotato/core/downloaders/utorrent/main.py b/couchpotato/core/downloaders/utorrent/main.py index e0d6a921..6a5e4257 100644 --- a/couchpotato/core/downloaders/utorrent/main.py +++ b/couchpotato/core/downloaders/utorrent/main.py @@ -115,6 +115,17 @@ class uTorrent(Downloader): return self.downloadReturnId(torrent_hash) + def test(self): + if self.connect(): + build_version = self.utorrent_api.get_build() + if not build_version: + return False + if build_version < 25406: # This build corresponds to version 3.0.0 stable + return False, 'Your uTorrent client is too old, please update to newest version.' + return True + + return False + def getAllDownloadStatus(self, ids): log.debug('Checking uTorrent download status.') @@ -322,3 +333,10 @@ class uTorrentAPI(object): def get_files(self, hash): action = 'action=getfiles&hash=%s' % hash return self._request(action) + + def get_build(self): + data = self._request('') + if not data: + return False + response = json.loads(data) + return int(response.get('build')) diff --git a/couchpotato/core/providers/torrent/ilovetorrents/main.py b/couchpotato/core/providers/torrent/ilovetorrents/main.py index 74bf0c40..d15be35b 100644 --- a/couchpotato/core/providers/torrent/ilovetorrents/main.py +++ b/couchpotato/core/providers/torrent/ilovetorrents/main.py @@ -12,12 +12,12 @@ log = CPLog(__name__) class ILoveTorrents(TorrentProvider): urls = { - 'download': 'http://www.ilovetorrents.me/%s', - 'detail': 'http://www.ilovetorrents.me/%s', - 'search': 'http://www.ilovetorrents.me/browse.php?search=%s&page=%s&cat=%s', - 'test': 'http://www.ilovetorrents.me/', - 'login': 'http://www.ilovetorrents.me/takelogin.php', - 'login_check': 'http://www.ilovetorrents.me' + 'download': 'https://www.ilovetorrents.me/%s', + 'detail': 'https//www.ilovetorrents.me/%s', + 'search': 'https://www.ilovetorrents.me/browse.php?search=%s&page=%s&cat=%s', + 'test': 'https://www.ilovetorrents.me/', + 'login': 'https://www.ilovetorrents.me/takelogin.php', + 'login_check': 'https://www.ilovetorrents.me' } cat_ids = [ diff --git a/couchpotato/core/providers/torrent/iptorrents/main.py b/couchpotato/core/providers/torrent/iptorrents/main.py index b4e038ca..5c22ae04 100644 --- a/couchpotato/core/providers/torrent/iptorrents/main.py +++ b/couchpotato/core/providers/torrent/iptorrents/main.py @@ -11,11 +11,11 @@ log = CPLog(__name__) class IPTorrents(TorrentProvider): urls = { - 'test': 'http://www.iptorrents.com/', - 'base_url': 'http://www.iptorrents.com', - 'login': 'http://www.iptorrents.com/torrents/', - 'login_check': 'http://www.iptorrents.com/inbox.php', - 'search': 'http://www.iptorrents.com/torrents/?l%d=1%s&q=%s&qf=ti&p=%d', + 'test': 'https://www.iptorrents.com/', + 'base_url': 'https://www.iptorrents.com', + 'login': 'https://www.iptorrents.com/torrents/', + 'login_check': 'https://www.iptorrents.com/inbox.php', + 'search': 'https://www.iptorrents.com/torrents/?l%d=1%s&q=%s&qf=ti&p=%d', } cat_ids = [ diff --git a/couchpotato/core/providers/torrent/thepiratebay/main.py b/couchpotato/core/providers/torrent/thepiratebay/main.py index 8ccefeb1..e355813b 100644 --- a/couchpotato/core/providers/torrent/thepiratebay/main.py +++ b/couchpotato/core/providers/torrent/thepiratebay/main.py @@ -31,15 +31,13 @@ class ThePirateBay(TorrentMagnetProvider): proxy_list = [ 'https://tpb.ipredator.se', 'https://thepiratebay.se', - 'https://depiraatbaai.be', - 'https://piratereverse.info', - 'https://tpb.pirateparty.org.uk', - 'https://argumentomteemigreren.nl', - 'https://livepirate.com', + 'http://pirateproxy.ca', + 'http://tpb.al', + 'http://www.tpb.gr', + 'http://nl.tpb.li', + 'http://proxybay.eu', 'https://www.getpirate.com', - 'https://tpb.partipirate.org', - 'https://tpb.piraten.lu', - 'https://kuiken.co', + 'http://pirateproxy.ca', ] def _searchOnTitle(self, title, movie, quality, results): diff --git a/couchpotato/core/providers/torrent/torrentshack/__init__.py b/couchpotato/core/providers/torrent/torrentshack/__init__.py index 0e552116..058236e4 100644 --- a/couchpotato/core/providers/torrent/torrentshack/__init__.py +++ b/couchpotato/core/providers/torrent/torrentshack/__init__.py @@ -11,7 +11,7 @@ config = [{ 'tab': 'searcher', 'list': 'torrent_providers', 'name': 'TorrentShack', - 'description': 'See TorrentShack', + 'description': 'See TorrentShack', 'options': [ { 'name': 'enabled', diff --git a/couchpotato/static/scripts/misc/downloaders.js b/couchpotato/static/scripts/misc/downloaders.js new file mode 100644 index 00000000..5127275c --- /dev/null +++ b/couchpotato/static/scripts/misc/downloaders.js @@ -0,0 +1,75 @@ +var DownloadersBase = new Class({ + + Implements: [Events], + + initialize: function(){ + var self = this; + + // Add test buttons to settings page + App.addEvent('load', self.addTestButtons.bind(self)); + + }, + + // Downloaders setting tests + addTestButtons: function(){ + var self = this; + + var setting_page = App.getPage('Settings'); + setting_page.addEvent('create', function(){ + Object.each(setting_page.tabs.downloaders.groups, self.addTestButton.bind(self)) + }) + + }, + + addTestButton: function(fieldset, plugin_name){ + var self = this, + button_name = self.testButtonName(fieldset); + + if(button_name.contains('Downloaders')) return; + + new Element('.ctrlHolder.test_button').adopt( + new Element('a.button', { + 'text': button_name, + 'events': { + 'click': function(){ + var button = fieldset.getElement('.test_button .button'); + button.set('text', 'Connecting...'); + + Api.request('download.'+plugin_name+'.test', { + 'onComplete': function(json){ + + button.set('text', button_name); + + if(json.success){ + var message = new Element('span.success', { + 'text': 'Connection successful' + }).inject(button, 'after') + } + else { + var msg_text = 'Connection failed. Check logs for details.'; + if(json.hasOwnProperty('msg')) msg_text = json.msg; + var message = new Element('span.failed', { + 'text': msg_text + }).inject(button, 'after') + } + + (function(){ + message.destroy(); + }).delay(3000) + } + }); + } + } + }) + ).inject(fieldset); + + }, + + testButtonName: function(fieldset){ + var name = String(fieldset.getElement('h2').innerHTML).substring(0,String(fieldset.getElement('h2').innerHTML).indexOf("= MIN_RTORRENT_VERSION