import json import traceback from couchpotato.core.downloaders.base import Downloader from couchpotato.core.helpers.encoding import isInt from couchpotato.core.helpers.variable import cleanHost from couchpotato.core.logger import CPLog import requests log = CPLog(__name__) autoload = 'Synology' class Synology(Downloader): protocol = ['nzb', 'torrent', 'torrent_magnet'] status_support = False def download(self, data = None, media = None, filedata = None): if not media: media = {} if not data: data = {} response = False log.error('Sending "%s" (%s) to Synology.', (data['name'], data['protocol'])) # 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 try: # Send request to Synology srpc = SynologyRPC(host[0], host[1], self.conf('username'), self.conf('password')) if data['protocol'] == 'torrent_magnet': log.info('Adding torrent URL %s', data['url']) response = srpc.create_task(url = data['url']) elif data['protocol'] in ['nzb', 'torrent']: log.info('Adding %s' % data['protocol']) if not filedata: log.error('No %s data found', data['protocol']) else: filename = data['name'] + '.' + data['protocol'] response = srpc.create_task(filename = filename, filedata = filedata) except: log.error('Exception while adding torrent: %s', traceback.format_exc()) 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() elif self.conf('use_for') == 'torrent': return ['torrent', 'torrent_magnet'] else: return ['nzb'] def isEnabled(self, manual = False, data = None): if not data: data = {} for_protocol = ['both'] if data and 'torrent' in data.get('protocol'): for_protocol.append('torrent') elif data: for_protocol.append(data.get('protocol')) return super(Synology, self).isEnabled(manual, data) and\ ((self.conf('use_for') in for_protocol)) class SynologyRPC(object): """SynologyRPC lite library""" def __init__(self, host = 'localhost', port = 5000, username = None, password = None): super(SynologyRPC, self).__init__() self.download_url = 'http://%s:%s/webapi/DownloadStation/task.cgi' % (host, port) self.auth_url = 'http://%s:%s/webapi/auth.cgi' % (host, port) self.username = username self.password = password self.session_name = 'DownloadStation' def _login(self): if self.username and self.password: args = {'api': 'SYNO.API.Auth', 'account': self.username, 'passwd': self.password, 'version': 2, 'method': 'login', 'session': self.session_name, 'format': 'sid'} response = self._req(self.auth_url, args) if response['success']: self.sid = response['data']['sid'] log.debug('sid=%s', self.sid) else: log.error('Couldn\'t login to Synology, %s', response) return response['success'] else: log.error('User or password missing, not using authentication.') return False def _logout(self): args = {'api':'SYNO.API.Auth', 'version':1, 'method':'logout', 'session':self.session_name, '_sid':self.sid} return self._req(self.auth_url, args) def _req(self, url, args, files = None): response = {'success': False} try: req = requests.post(url, data = args, files = files) req.raise_for_status() response = json.loads(req.text) if response['success']: log.info('Synology action successfull') return response except requests.ConnectionError as err: log.error('Synology connection error, check your config %s', err) except requests.HTTPError as err: log.error('SynologyRPC HTTPError: %s', err) except Exception as err: log.error('Exception: %s', err) finally: return response def create_task(self, url = None, filename = None, filedata = None): """ Creates new download task in Synology DownloadStation. Either specify url or pair (filename, filedata). Returns True if task was created, False otherwise """ result = False # login if self._login(): args = {'api': 'SYNO.DownloadStation.Task', 'version': '1', 'method': 'create', '_sid': self.sid} if url: log.info('Login success, adding torrent URI') args['uri'] = url response = self._req(self.download_url, args = args) log.info('Response: %s', response) result = response['success'] elif filename and filedata: log.info('Login success, adding torrent') files = {'file': (filename, filedata)} response = self._req(self.download_url, args = args, files = files) log.info('Response: %s', response) result = response['success'] else: log.error('Invalid use of SynologyRPC.create_task: either url or filename+filedata must be specified') self._logout() return result def test(self): return bool(self._login()) config = [{ 'name': 'synology', 'groups': [ { 'tab': 'downloaders', 'list': 'download_providers', 'name': 'synology', 'label': 'Synology', 'description': 'Use Synology Download Station to download.', 'wizard': True, 'options': [ { 'name': 'enabled', 'default': 0, 'type': 'enabler', 'radio_group': 'nzb,torrent', }, { 'name': 'host', 'default': 'localhost:5000', 'description': 'Hostname with port. Usually localhost:5000', }, { 'name': 'username', }, { 'name': 'password', 'type': 'password', }, { 'name': 'use_for', 'label': 'Use for', 'default': 'both', 'type': 'dropdown', 'values': [('usenet & torrents', 'both'), ('usenet', 'nzb'), ('torrent', 'torrent')], }, { 'name': 'manual', 'default': 0, 'type': 'bool', 'advanced': True, 'description': 'Disable this downloader for automated searches, but use it when I manually send a release.', }, ], } ], }]