示例#1
0
    def _get_auth(self):
        if self.auth is not None:
            return self.auth

        if not self.host:
            return

        tp_kwargs = {}
        if app.TORRENT_AUTH_TYPE != 'none':
            tp_kwargs['authtype'] = app.TORRENT_AUTH_TYPE

        if not app.TORRENT_VERIFY_CERT:
            tp_kwargs['check_ssl_cert'] = False
        else:
            if app.SSL_CA_BUNDLE:
                tp_kwargs['check_ssl_cert'] = app.SSL_CA_BUNDLE

        if self.username and self.password:
            self.auth = RTorrent(self.host,
                                 self.username,
                                 self.password,
                                 True,
                                 tp_kwargs=tp_kwargs)
        else:
            self.auth = RTorrent(self.host, None, None, True)

        return self.auth
示例#2
0
    def _get_auth(self):

        if self.auth is not None:
            return self.auth

        if not self.host:
            return

        self.host = self.host.rstrip('/')

        tp_kwargs = {}
        if settings.TORRENT_AUTH_TYPE and settings.TORRENT_AUTH_TYPE.lower(
        ) != 'none':
            tp_kwargs['authtype'] = settings.TORRENT_AUTH_TYPE

        if not settings.TORRENT_VERIFY_CERT:
            tp_kwargs['check_ssl_cert'] = False

        if self.username and self.password:
            self.auth = RTorrent(self.host,
                                 self.username,
                                 self.password,
                                 True,
                                 tp_kwargs=tp_kwargs or None)
        else:
            self.auth = RTorrent(self.host,
                                 None,
                                 None,
                                 True,
                                 tp_kwargs=tp_kwargs or None)

        return self.auth
示例#3
0
    def connect(self, reconnect=False):
        # Already connected?
        if not reconnect and self.rt is not None:
            return self.rt

        url = cleanHost(self.conf('host'), protocol=True, ssl=self.conf('ssl'))

        # Automatically add '+https' to 'httprpc' protocol if SSL is enabled
        if self.conf('ssl') and url.startswith('httprpc://'):
            url = url.replace('httprpc://', 'httprpc+https://')

        parsed = urlparse(url)

        # rpc_url is only used on http/https scgi pass-through
        if parsed.scheme in ['http', 'https']:
            url += self.conf('rpc_url')

        # Construct client
        self.rt = RTorrent(url, self.getAuth(), verify_ssl=self.getVerifySsl())

        self.error_msg = ''
        try:
            self.rt.connection.verify()
        except AssertionError as e:
            self.error_msg = e.message
            self.rt = None

        return self.rt
示例#4
0
    def _get_auth(self):
        self.auth = None

        if self.auth is not None:
            return self.auth

        if not self.host:
            return

        tp_kwargs = {}
        if sickbeard.TORRENT_AUTH_TYPE is not 'none':
            tp_kwargs[b'authtype'] = sickbeard.TORRENT_AUTH_TYPE

        if not sickbeard.TORRENT_VERIFY_CERT:
            tp_kwargs[b'check_ssl_cert'] = False

        if self.username and self.password:
            self.auth = RTorrent(self.host,
                                 self.username,
                                 self.password,
                                 True,
                                 tp_kwargs=tp_kwargs)
        else:
            self.auth = RTorrent(self.host, None, None, True)

        return self.auth
示例#5
0
    def _get_auth(self):
        if self.auth is not None:
            return self.auth

        if not self.host:
            return

        tp_kwargs = {}
        if app.TORRENT_AUTH_TYPE != 'none':
            tp_kwargs['authtype'] = app.TORRENT_AUTH_TYPE

        if not app.TORRENT_VERIFY_CERT:
            tp_kwargs['check_ssl_cert'] = False
        else:
            if app.SSL_CA_BUNDLE:
                tp_kwargs['check_ssl_cert'] = app.SSL_CA_BUNDLE

        try:
            if self.username and self.password:
                self.auth = RTorrent(self.host,
                                     self.username,
                                     self.password,
                                     True,
                                     tp_kwargs=tp_kwargs)
            else:
                self.auth = RTorrent(self.host, None, None, True)
        except Exception as error:  # No request/connection specific exception thrown.
            raise DownloadClientConnectionException(
                f'Unable to authenticate with rtorrent client: {error}')

        return self.auth
示例#6
0
    def _get_auth(self):
        self.auth = None

        if self.auth is not None:
            return self.auth

        if not self.host:
            return

        sp_kwargs = {}
        if sickrage.TORRENT_AUTH_TYPE != 'None':
            sp_kwargs[b'authtype'] = sickrage.TORRENT_AUTH_TYPE

        if not sickrage.TORRENT_VERIFY_CERT:
            sp_kwargs[b'check_ssl_cert'] = False

        if self.username and self.password:
            url_parts = self.host.split('//')
            self.auth = RTorrent(
                url_parts[0] +
                "{0}:{1}@".format(self.username, self.password) + url_parts[1])
        else:
            self.auth = RTorrent(self.host)

        return self.auth
示例#7
0
    def connect(self, reconnect=False):
        # Already connected?
        if not reconnect and self.rt is not None:
            return self.rt

        url = cleanHost(self.conf('host'), protocol=True, ssl=self.conf('ssl'))
        parsed = urlparse(url)

        # rpc_url is only used on http/https scgi pass-through
        if parsed.scheme in ['http', 'https']:
            url += self.conf('rpc_url')

        if self.conf('username') and self.conf('password'):
            self.rt = RTorrent(url, self.conf('username'),
                               self.conf('password'))
        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
示例#8
0
    def setUp(self):
        self.rt = RTorrent()
        self.test_file = "tests/tester.torrent"
        self.test_magnet = "magnet:?xt=urn:btih:f2ed240912dc324d6a30de6811a8747f80b9722d&dn=The+Wolverine+2013+DVDRip+x264+AC3-EVO&tr=udp%3A%2F%2Ftracker.openbittorrent.com%3A80&tr=udp%3A%2F%2Ftracker.publicbt.com%3A80&tr=udp%3A%2F%2Ftracker.istole.it%3A6969&tr=udp%3A%2F%2Ftracker.ccc.de%3A80&tr=udp%3A%2F%2Fopen.demonii.com%3A1337"

        files = self.rt.get_active_infohashes()
        for f in files:
            self.rt.erase(f)
示例#9
0
    def _authenticate(self):
        """
        Setup connection to rTorrent XMLRPC server
        :return:
        """

        try:
            self.rtorrent = RTorrent(self.url)
        except ConnectionRefusedError as e:
            self.send_log('Failed to connect to rTorrent.  Aborting',
                          'critical')
            sys.exit(1)

        self.send_log('Successfully connected to rTorrent', 'info')
示例#10
0
    def connect(self):
        # Already connected?
        if self.rt is not None:
            return self.rt

        url = cleanHost(self.conf('host'), protocol=True,
                        ssl=self.conf('ssl')) + self.conf('rpc_url')

        if self.conf('username') and self.conf('password'):
            self.rt = RTorrent(url, self.conf('username'),
                               self.conf('password'))
        else:
            self.rt = RTorrent(url)

        return self.rt
示例#11
0
    def connect(self):
        # Already connected?
        if self.rt is not None:
            return self.rt

        # Ensure url is set
        if not self.conf('url'):
            log.error(
                'Config properties are not filled in correctly, url is missing.'
            )
            return False

        if self.conf('username') and self.conf('password'):
            self.rt = RTorrent(self.conf('url'), self.conf('username'),
                               self.conf('password'))
        else:
            self.rt = RTorrent(self.conf('url'))

        return self.rt
示例#12
0
def test_RTorrent(socketpath):
    rtorrent = RTorrent(socketpath)
    assert rtorrent.test_connection() is True
    listViews = rtorrent.get_views()
    print(listViews)
    t0 = time()
    listTorrents = rtorrent.get_torrents()
    t1 = time()
    print(listTorrents, t1 - t0)
    listTorrents = rtorrent.get_torrents()
    t2 = time()
    print(listTorrents, t2 - t1)
示例#13
0
def test_Torrent(socketpath):
    rtorrent = RTorrent(socketpath)
    t = Torrent(rtorrent, info_hash="42B7EFAB0D48757C6D7906272733BD22B57BB182")
示例#14
0
class rTorrentAPI(GenericClient):
    def __init__(self, host=None, username=None, password=None):
        super(rTorrentAPI, self).__init__('rTorrent', host, username, password)

    def _get_auth(self):
        self.auth = None

        if self.auth is not None:
            return self.auth

        if not self.host:
            return

        tp_kwargs = {}
        if sickbeard.TORRENT_AUTH_TYPE is not 'none':
            tp_kwargs[b'authtype'] = sickbeard.TORRENT_AUTH_TYPE

        if not sickbeard.TORRENT_VERIFY_CERT:
            tp_kwargs[b'check_ssl_cert'] = False

        if self.username and self.password:
            self.auth = RTorrent(self.host,
                                 self.username,
                                 self.password,
                                 True,
                                 tp_kwargs=tp_kwargs)
        else:
            self.auth = RTorrent(self.host, None, None, True)

        return self.auth

    def _add_torrent_uri(self, result):

        if not self.auth:
            return False

        if not result:
            return False

        try:
            # Send magnet to rTorrent
            torrent = self.auth.load_magnet(result.url, result.hash)

            if not torrent:
                return False

            # Set label
            label = sickbeard.TORRENT_LABEL
            if result.show.is_anime:
                label = sickbeard.TORRENT_LABEL_ANIME
            if label:
                torrent.set_custom(1, label)

            if sickbeard.TORRENT_PATH:
                torrent.set_directory(sickbeard.TORRENT_PATH)

            # Start torrent
            torrent.start()

            return True

        except Exception:
            logging.debug(traceback.format_exc())
            return False

    def _add_torrent_file(self, result):

        if not self.auth:
            return False

        if not result:
            return False

            # group_name = 'sb_test'.lower() ##### Use provider instead of _test
            # if not self._set_torrent_ratio(group_name):
            # return False

        # Send request to rTorrent
        try:
            # Send torrent to rTorrent
            torrent = self.auth.load_torrent(result.content)

            if not torrent:
                return False

            # Set label
            label = sickbeard.TORRENT_LABEL
            if result.show.is_anime:
                label = sickbeard.TORRENT_LABEL_ANIME
            if label:
                torrent.set_custom(1, label)

            if sickbeard.TORRENT_PATH:
                torrent.set_directory(sickbeard.TORRENT_PATH)

            # Set Ratio Group
            # torrent.set_visible(group_name)

            # Start torrent
            torrent.start()

            return True

        except Exception:
            logging.debug(traceback.format_exc())
            return False

    def _set_torrent_ratio(self, name):

        # if not name:
        # return False
        #
        # if not self.auth:
        # return False
        #
        # views = self.auth.get_views()
        #
        # if name not in views:
        # self.auth.create_group(name)

        # group = self.auth.get_group(name)

        # ratio = int(float(sickbeard.TORRENT_RATIO) * 100)
        #
        # try:
        # if ratio > 0:
        #
        # # Explicitly set all group options to ensure it is setup correctly
        # group.set_upload('1M')
        # group.set_min(ratio)
        # group.set_max(ratio)
        # group.set_command('d.stop')
        # group.enable()
        # else:
        # # Reset group action and disable it
        # group.set_command()
        # group.disable()
        #
        # except:
        # return False

        return True

    def testAuthentication(self):
        try:
            self._get_auth()

            if self.auth is not None:
                return True, 'Success: Connected and Authenticated'
            else:
                return False, 'Error: Unable to get ' + self.name + ' Authentication, check your config!'
        except Exception:
            return False, 'Error: Unable to connect to ' + self.name
示例#15
0
class rTorrent(Downloader):

    protocol = ['torrent', 'torrent_magnet']
    rt = None

    # Migration url to host options
    def __init__(self):
        super(rTorrent, self).__init__()

        addEvent('app.load', self.migrate)

    def migrate(self):

        url = self.conf('url')
        if url:
            host_split = splitString(url.split('://')[-1], split_on='/')

            self.conf('ssl', value=url.startswith('https'))
            self.conf('host', value=host_split[0].strip())
            self.conf('rpc_url', value='/'.join(host_split[1:]))

            self.deleteConf('url')

    def connect(self):
        # Already connected?
        if self.rt is not None:
            return self.rt

        url = cleanHost(self.conf('host'), protocol=True, ssl=self.conf(
            'ssl')) + '/' + self.conf('rpc_url').strip('/ ') + '/'

        if self.conf('username') and self.conf('password'):
            self.rt = RTorrent(url, self.conf('username'),
                               self.conf('password'))
        else:
            self.rt = RTorrent(url)

        return self.rt

    def _update_provider_group(self, name, data):
        if data.get('seed_time'):
            log.info('seeding time ignored, not supported')

        if not name:
            return False

        if not self.connect():
            return False

        views = self.rt.get_views()

        if name not in views:
            self.rt.create_group(name)

        group = self.rt.get_group(name)

        try:
            if data.get('seed_ratio'):
                ratio = int(float(data.get('seed_ratio')) * 100)
                log.debug('Updating provider ratio to %s, group name: %s',
                          (ratio, name))

                # Explicitly set all group options to ensure it is setup correctly
                group.set_upload('1M')
                group.set_min(ratio)
                group.set_max(ratio)
                group.set_command('d.stop')
                group.enable()
            else:
                # Reset group action and disable it
                group.set_command()
                group.disable()
        except MethodError, err:
            log.error('Unable to set group options: %s', err.msg)
            return False

        return True
示例#16
0
class rTorrent(DownloaderBase):

    protocol = ['torrent', 'torrent_magnet']
    rt = None
    error_msg = ''

    # Migration url to host options
    def __init__(self):
        super(rTorrent, self).__init__()

        addEvent('app.load', self.migrate)
        addEvent('setting.save.rtorrent.*.after', self.settingsChanged)

    def migrate(self):

        url = self.conf('url')
        if url:
            host_split = splitString(url.split('://')[-1], split_on='/')

            self.conf('ssl', value=url.startswith('https'))
            self.conf('host', value=host_split[0].strip())
            self.conf('rpc_url', value='/'.join(host_split[1:]))

            self.deleteConf('url')

    def settingsChanged(self):
        # Reset active connection if settings have changed
        if self.rt:
            log.debug('Settings have changed, closing active connection')

        self.rt = None
        return True

    def getAuth(self):
        if not self.conf('username') or not self.conf('password'):
            # Missing username or password parameter
            return None

        # Build authentication tuple
        return (self.conf('authentication'), self.conf('username'),
                self.conf('password'))

    def getVerifySsl(self):
        # Ensure verification has been enabled
        if not self.conf('ssl_verify'):
            return False

        # Use ca bundle if defined
        ca_bundle = self.conf('ssl_ca_bundle')

        if ca_bundle and os.path.exists(ca_bundle):
            return ca_bundle

        # Use default ssl verification
        return True

    def connect(self, reconnect=False):
        # Already connected?
        if not reconnect and self.rt is not None:
            return self.rt

        url = cleanHost(self.conf('host'), protocol=True, ssl=self.conf('ssl'))

        # Automatically add '+https' to 'httprpc' protocol if SSL is enabled
        if self.conf('ssl') and url.startswith('httprpc://'):
            url = url.replace('httprpc://', 'httprpc+https://')

        parsed = urlparse(url)

        # rpc_url is only used on http/https scgi pass-through
        if parsed.scheme in ['http', 'https']:
            url += self.conf('rpc_url')

        # Construct client
        self.rt = RTorrent(url, self.getAuth(), verify_ssl=self.getVerifySsl())

        self.error_msg = ''
        try:
            self.rt.connection.verify()
        except AssertionError as e:
            self.error_msg = e.message
            self.rt = None

        return self.rt

    def test(self):
        """ Check if connection works
        :return: bool
        """

        if self.connect(True):
            return True

        if self.error_msg:
            return False, 'Connection failed: ' + self.error_msg

        return False

    def download(self, data=None, media=None, filedata=None):
        """ Send a torrent/nzb file to the downloader

        :param data: dict returned from provider
            Contains the release information
        :param media: media dict with information
            Used for creating the filename when possible
        :param filedata: downloaded torrent/nzb filedata
            The file gets downloaded in the searcher and send to this function
            This is done to have failed checking before using the downloader, so the downloader
            doesn't need to worry about that
        :return: boolean
            One faile returns false, but the downloaded should log his own errors
        """

        if not media: media = {}
        if not data: data = {}

        log.debug('Sending "%s" to rTorrent.', (data.get('name')))

        if not self.connect():
            return False

        torrent_params = {}
        if self.conf('label'):
            torrent_params['label'] = self.conf('label')

        if not filedata and data.get('protocol') == 'torrent':
            log.error('Failed sending torrent, no data')
            return False

        # Try download magnet torrents
        if data.get('protocol') == 'torrent_magnet':
            filedata = self.magnetToTorrent(data.get('url'))

            if filedata is False:
                return False

            data['protocol'] = 'torrent'

        info = bdecode(filedata)["info"]
        torrent_hash = sha1(bencode(info)).hexdigest().upper()

        # Convert base 32 to hex
        if len(torrent_hash) == 32:
            torrent_hash = b16encode(b32decode(torrent_hash))

        # Send request to rTorrent
        try:
            # Send torrent to rTorrent
            torrent = self.rt.load_torrent(filedata, verify_retries=10)

            if not torrent:
                log.error('Unable to find the torrent, did it fail to load?')
                return False

            # Set label
            if self.conf('label'):
                torrent.set_custom(1, self.conf('label'))

            if self.conf('directory'):
                torrent.set_directory(self.conf('directory'))

            # Start torrent
            if not self.conf('paused', default=0):
                torrent.start()

            return self.downloadReturnId(torrent_hash)
        except Exception as err:
            log.error('Failed to send torrent to rTorrent: %s', err)
            return False

    def getTorrentStatus(self, torrent):
        if not torrent.complete:
            return 'busy'

        if torrent.open:
            return 'seeding'

        return 'completed'

    def getAllDownloadStatus(self, ids):
        """ Get status of all active downloads

        :param ids: list of (mixed) downloader ids
            Used to match the releases for this downloader as there could be
            other downloaders active that it should ignore
        :return: list of releases
        """

        log.debug('Checking rTorrent download status.')

        if not self.connect():
            return []

        try:
            torrents = self.rt.get_torrents()

            release_downloads = ReleaseDownloadList(self)

            for torrent in torrents:
                if torrent.info_hash in ids:
                    torrent_directory = os.path.normpath(torrent.directory)
                    torrent_files = []

                    for file in torrent.get_files():
                        if not os.path.normpath(
                                file.path).startswith(torrent_directory):
                            file_path = os.path.join(torrent_directory,
                                                     file.path.lstrip('/'))
                        else:
                            file_path = file.path

                        torrent_files.append(sp(file_path))

                    release_downloads.append({
                        'id':
                        torrent.info_hash,
                        'name':
                        torrent.name,
                        'status':
                        self.getTorrentStatus(torrent),
                        'seed_ratio':
                        torrent.ratio,
                        'original_status':
                        torrent.state,
                        'timeleft':
                        str(
                            timedelta(seconds=float(torrent.left_bytes) /
                                      torrent.down_rate))
                        if torrent.down_rate > 0 else -1,
                        'folder':
                        sp(torrent.directory),
                        'files':
                        torrent_files
                    })

            return release_downloads

        except Exception as err:
            log.error('Failed to get status from rTorrent: %s', err)
            return []

    def pause(self, release_download, pause=True):
        if not self.connect():
            return False

        torrent = self.rt.find_torrent(release_download['id'])
        if torrent is None:
            return False

        if pause:
            return torrent.pause()
        return torrent.resume()

    def removeFailed(self, release_download):
        log.info('%s failed downloading, deleting...',
                 release_download['name'])
        return self.processComplete(release_download, delete_files=True)

    def processComplete(self, release_download, delete_files):
        log.debug(
            'Requesting rTorrent to remove the torrent %s%s.',
            (release_download['name'],
             ' and cleanup the downloaded files' if delete_files else ''))

        if not self.connect():
            return False

        torrent = self.rt.find_torrent(release_download['id'])

        if torrent is None:
            return False

        if delete_files:
            for file_item in torrent.get_files(
            ):  # will only delete files, not dir/sub-dir
                os.unlink(os.path.join(torrent.directory, file_item.path))

            if torrent.is_multi_file() and torrent.directory.endswith(
                    torrent.name):
                # Remove empty directories bottom up
                try:
                    for path, _, _ in os.walk(sp(torrent.directory),
                                              topdown=False):
                        os.rmdir(path)
                except OSError:
                    log.info(
                        'Directory "%s" contains extra files, unable to remove',
                        torrent.directory)

        torrent.erase()  # just removes the torrent, doesn't delete data

        return True
示例#17
0
class rTorrentClient(TorrentClient):
    def __init__(self,
                 logger,
                 username=None,
                 password=None,
                 url=None,
                 hostname=None):

        TorrentClient.__init__(self,
                               logger,
                               username=username,
                               password=password,
                               url=url,
                               hostname=hostname)
        self.torrent_client = 'rTorrent'
        self._authenticate()
        self.rtorrent = None

        self._authenticate()

    def _authenticate(self):
        """
        Setup connection to rTorrent XMLRPC server
        :return:
        """

        try:
            self.rtorrent = RTorrent(self.url)
        except ConnectionRefusedError as e:
            self.send_log('Failed to connect to rTorrent.  Aborting',
                          'critical')
            sys.exit(1)

        self.send_log('Successfully connected to rTorrent', 'info')

    def _build_torrent_list(self, torrents):
        """
        Take the resulting torrent list and create a consistent structure shared through all clients
        :return:
        """
        self.send_log(
            'Structuring list of ' + str(len(torrents)) + ' torrent(s)',
            'debug')

        for torrent in torrents:
            self.torrent_list[torrent.info_hash] = {}
            self.torrent_list[torrent.info_hash]['name'] = torrent.name
            self.torrent_list[
                torrent.info_hash]['total_size'] = torrent.size_bytes
            self.torrent_list[torrent.info_hash]['progress'] = round(
                (torrent.bytes_done / torrent.size_bytes * 100), 2)
            self.torrent_list[
                torrent.info_hash]['total_downloaded'] = torrent.bytes_done
            self.torrent_list[
                torrent.info_hash]['total_uploaded'] = torrent.get_up_total()
            self.torrent_list[torrent.info_hash]['ratio'] = torrent.ratio
            self.torrent_list[torrent.info_hash]['total_seeds'] = 'N/A'
            self.torrent_list[torrent.info_hash]['state'] = torrent.get_state()
            self.torrent_list[torrent.info_hash]['tracker'] = urlsplit(
                torrent.get_trackers()[0].url).netloc
            self.torrent_list[
                torrent.info_hash]['total_files'] = torrent.size_files

    def get_all_torrents(self):
        """
        Return list of all torrents
        :return:
        """
        self._authenticate(
        )  # We need to get another Rtorrent object so we get a fresh list of torrents
        self._build_torrent_list(self.rtorrent.get_torrents())
示例#18
0
def xhr_rtorrentdl():

    # url qualification
    def url_qualify(url_proto, url_host, url_port):

        url_host_part = str(url_host).partition('/')

        # validate host (kinda... just make sure it's not empty)
        if url_host_part[0]:

            # validate port
            if not url_port:

                # for http and https we can assume default service ports
                if url_proto == 'http':
                    url_port = 80
                elif url_proto == 'https':
                    url_port = 443
                else:
                    raise Exception('port must be defined for protocol %s' %
                                    (url_proto))

            else:
                try:
                    url_port = int(url_port)
                except ValueError:
                    raise Exception('port must be a numeric value')

            url_qualified = '%s://%s:%i%s%s' % (url_proto, url_host_part[0],
                                                url_port, url_host_part[1],
                                                url_host_part[2])

            return url_qualified

        else:
            raise Exception('invalid host: %s' % (url_host[0]))

    # initialize empty list, which will be later populated with listing
    # of active torrents
    torrentlist = list()

    # connection flag
    connected = False

    # global rates
    down_rate = 0.0
    up_rate = 0.0

    rtorrent_url = None
    rtorrent_user = None
    rtorrent_password = None

    try:
        if get_setting_value('rtorrent_host') is not None:
            rtorrent_url = url_qualify(get_setting_value('rtorrent_proto'),
                                       get_setting_value('rtorrent_host'),
                                       get_setting_value('rtorrent_port'))
    except Exception as ex:
        log_error(ex)

    try:
        if rtorrent_url:
            # user/password login is not implemented for scgi
            if get_setting_value('rtorrent_proto') != 'scgi':
                rtorrent_user = get_setting_value('rtorrent_user')
                rtorrent_password = get_setting_value('rtorrent_password')

            client = RTorrent(rtorrent_url, rtorrent_user, rtorrent_password,
                              True)

            if client is not None:
                connected = True
                down_rate = client.get_down_rate()
                up_rate = client.get_up_rate()

            # loop through each job and add all torrents to torrentlist()
            for torrent in client.get_torrents():
                # friendly status and time left
                time_left = -1
                if torrent.complete:
                    if torrent.active:
                        status = 'seeding'
                    else:
                        status = 'done'
                else:
                    if torrent.active:
                        if torrent.down_rate > 0:
                            time_left = str(
                                timedelta(seconds=round(
                                    float(torrent.left_bytes) /
                                    torrent.down_rate)))
                            status = 'leeching'
                        else:
                            status = 'waiting'
                    else:
                        status = 'inactive'

                # get torrent file list
                # FIXME takes too much time and is not used anyway for now
                #torrent_filelist = []
                #for file_current in torrent.get_files():
                #	torrent_filelist.append(os.path.join(torrent.directory,file_current.path))

                # what's left?
                progress = float(100.0 / torrent.size_bytes *
                                 (torrent.size_bytes - torrent.left_bytes))

                # append to list
                torrentlist.append({
                    'name': torrent.name,
                    'info_hash': torrent.info_hash,
                    'status': status,
                    'state': torrent.state,
                    'progress': progress,
                    'time_left': time_left,
                    'down_rate': torrent.down_rate,
                    'up_rate': torrent.up_rate,
                    'ratio': torrent.ratio
                    #	'folder': torrent.directory,
                    #	'files': '|'.join(torrent_filelist)
                })

            # no torrents -> empty list
            if not torrentlist.__len__():
                torrentlist = None

    except Exception as ex:
        log_error(ex)
        torrentlist = None

    return render_template(
        'rtorrentdl.html',
        connected=connected,
        torrentlist_scroll=get_setting_value('rtorrent_list_scroll'),
        torrentlist=torrentlist,
        down_rate=down_rate,
        up_rate=up_rate)
示例#19
0
class rTorrent(Downloader):

    protocol = ['torrent', 'torrent_magnet']
    rt = None

    # Migration url to host options
    def __init__(self):
        super(rTorrent, self).__init__()

        addEvent('app.load', self.migrate)

    def migrate(self):

        url = self.conf('url')
        if url:
            host_split = splitString(url.split('://')[-1], split_on='/')

            self.conf('ssl', value=url.startswith('https'))
            self.conf('host', value=host_split[0].strip())
            self.conf('rpc_url', value='/'.join(host_split[1:]))

            self.deleteConf('url')

    def connect(self):
        # Already connected?
        if self.rt is not None:
            return self.rt

        url = cleanHost(self.conf('host'), protocol=True,
                        ssl=self.conf('ssl')) + self.conf('rpc_url')

        if self.conf('username') and self.conf('password'):
            self.rt = RTorrent(url, self.conf('username'),
                               self.conf('password'))
        else:
            self.rt = RTorrent(url)

        return self.rt

    def _update_provider_group(self, name, data):
        if data.get('seed_time'):
            log.info('seeding time ignored, not supported')

        if not name:
            return False

        if not self.connect():
            return False

        views = self.rt.get_views()

        if name not in views:
            self.rt.create_group(name)

        group = self.rt.get_group(name)

        try:
            if data.get('seed_ratio'):
                ratio = int(float(data.get('seed_ratio')) * 100)
                log.debug('Updating provider ratio to %s, group name: %s',
                          (ratio, name))

                # Explicitly set all group options to ensure it is setup correctly
                group.set_upload('1M')
                group.set_min(ratio)
                group.set_max(ratio)
                group.set_command('d.stop')
                group.enable()
            else:
                # Reset group action and disable it
                group.set_command()
                group.disable()
        except MethodError as err:
            log.error('Unable to set group options: %s', err.msg)
            return False

        return True

    def download(self, data=None, media=None, filedata=None):
        if not media: media = {}
        if not data: data = {}

        log.debug('Sending "%s" to rTorrent.', (data.get('name')))

        if not self.connect():
            return False

        group_name = 'cp_' + data.get('provider').lower()
        if not self._update_provider_group(group_name, data):
            return False

        torrent_params = {}
        if self.conf('label'):
            torrent_params['label'] = self.conf('label')

        if not filedata and data.get('protocol') == 'torrent':
            log.error('Failed sending torrent, no data')
            return False

        # Try download magnet torrents
        if data.get('protocol') == 'torrent_magnet':
            filedata = self.magnetToTorrent(data.get('url'))

            if filedata is False:
                return False

            data['protocol'] = 'torrent'

        info = bdecode(filedata)["info"]
        torrent_hash = sha1(bencode(info)).hexdigest().upper()

        # Convert base 32 to hex
        if len(torrent_hash) == 32:
            torrent_hash = b16encode(b32decode(torrent_hash))

        # Send request to rTorrent
        try:
            # Send torrent to rTorrent
            torrent = self.rt.load_torrent(filedata, verify_retries=10)

            if not torrent:
                log.error('Unable to find the torrent, did it fail to load?')
                return False

            # Set label
            if self.conf('label'):
                torrent.set_custom(1, self.conf('label'))

            if self.conf('directory'):
                torrent.set_directory(self.conf('directory'))

            # Set Ratio Group
            torrent.set_visible(group_name)

            # Start torrent
            if not self.conf('paused', default=0):
                torrent.start()

            return self.downloadReturnId(torrent_hash)
        except Exception as err:
            log.error('Failed to send torrent to rTorrent: %s', err)
            return False

    def getAllDownloadStatus(self, ids):
        log.debug('Checking rTorrent download status.')

        if not self.connect():
            return []

        try:
            torrents = self.rt.get_torrents()

            release_downloads = ReleaseDownloadList(self)

            for torrent in torrents:
                if torrent.info_hash in ids:
                    torrent_directory = os.path.normpath(torrent.directory)
                    torrent_files = []

                    for file in torrent.get_files():
                        if not os.path.normpath(
                                file.path).startswith(torrent_directory):
                            file_path = os.path.join(torrent_directory,
                                                     file.path.lstrip('/'))
                        else:
                            file_path = file.path

                        torrent_files.append(sp(file_path))

                    status = 'busy'
                    if torrent.complete:
                        if torrent.active:
                            status = 'seeding'
                        else:
                            status = 'completed'

                    release_downloads.append({
                        'id':
                        torrent.info_hash,
                        'name':
                        torrent.name,
                        'status':
                        status,
                        'seed_ratio':
                        torrent.ratio,
                        'original_status':
                        torrent.state,
                        'timeleft':
                        str(
                            timedelta(seconds=float(torrent.left_bytes) /
                                      torrent.down_rate))
                        if torrent.down_rate > 0 else -1,
                        'folder':
                        sp(torrent.directory),
                        'files':
                        '|'.join(torrent_files)
                    })

            return release_downloads

        except Exception as err:
            log.error('Failed to get status from rTorrent: %s', err)
            return []

    def pause(self, release_download, pause=True):
        if not self.connect():
            return False

        torrent = self.rt.find_torrent(release_download['id'])
        if torrent is None:
            return False

        if pause:
            return torrent.pause()
        return torrent.resume()

    def removeFailed(self, release_download):
        log.info('%s failed downloading, deleting...',
                 release_download['name'])
        return self.processComplete(release_download, delete_files=True)

    def processComplete(self, release_download, delete_files):
        log.debug(
            'Requesting rTorrent to remove the torrent %s%s.',
            (release_download['name'],
             ' and cleanup the downloaded files' if delete_files else ''))

        if not self.connect():
            return False

        torrent = self.rt.find_torrent(release_download['id'])

        if torrent is None:
            return False

        if delete_files:
            for file_item in torrent.get_files(
            ):  # will only delete files, not dir/sub-dir
                os.unlink(os.path.join(torrent.directory, file_item.path))

            if torrent.is_multi_file() and torrent.directory.endswith(
                    torrent.name):
                # Remove empty directories bottom up
                try:
                    for path, _, _ in os.walk(torrent.directory,
                                              topdown=False):
                        os.rmdir(path)
                except OSError:
                    log.info(
                        'Directory "%s" contains extra files, unable to remove',
                        torrent.directory)

        torrent.erase()  # just removes the torrent, doesn't delete data

        return True
示例#20
0
class RTorrentAPI(GenericClient):
    """rTorrent API class."""

    def __init__(self, host=None, username=None, password=None):
        """Constructor.

        :param host:
        :type host: string
        :param username:
        :type username: string
        :param password:
        :type password: string
        """
        super(RTorrentAPI, self).__init__('rTorrent', host, username, password)

    def _get_auth(self):
        if self.auth is not None:
            return self.auth

        if not self.host:
            return

        tp_kwargs = {}
        if app.TORRENT_AUTH_TYPE != 'none':
            tp_kwargs['authtype'] = app.TORRENT_AUTH_TYPE

        if not app.TORRENT_VERIFY_CERT:
            tp_kwargs['check_ssl_cert'] = False
        else:
            if app.SSL_CA_BUNDLE:
                tp_kwargs['check_ssl_cert'] = app.SSL_CA_BUNDLE

        if self.username and self.password:
            self.auth = RTorrent(self.host, self.username, self.password, True, tp_kwargs=tp_kwargs)
        else:
            self.auth = RTorrent(self.host, None, None, True)

        return self.auth

    @staticmethod
    def _get_params(result):
        params = []

        # Set label
        label = app.TORRENT_LABEL
        if result.series.is_anime:
            label = app.TORRENT_LABEL_ANIME
        if label:
            params.append('d.custom1.set={0}'.format(label))

        if app.TORRENT_PATH:
            params.append('d.directory.set={0}'.format(app.TORRENT_PATH))

        return params

    def _add_torrent_uri(self, result):

        if not (self.auth or result):
            return False

        try:
            params = self._get_params(result)
            # Send magnet to rTorrent and start it
            torrent = self.auth.load_magnet(result.url, result.hash, start=True, params=params)
            if not torrent:
                return False

        except Exception as msg:
            log.warning('Error while sending torrent: {error!r}',
                        {'error': msg})
            return False
        else:
            return True

    def _add_torrent_file(self, result):

        if not (self.auth or result):
            return False

        try:
            params = self._get_params(result)
            # Send torrent to rTorrent and start it
            torrent = self.auth.load_torrent(result.content, start=True, params=params)
            if not torrent:
                return False

        except Exception as msg:
            log.warning('Error while sending torrent: {error!r}',
                        {'error': msg})
            return False
        else:
            return True

    def test_authentication(self):
        """Test connection using authentication.

        :return:
        :rtype: tuple(bool, str)
        """
        try:
            self.auth = None
            self._get_auth()
        except Exception:  # pylint: disable=broad-except
            return False, 'Error: Unable to connect to {name}'.format(name=self.name)
        else:
            if self.auth is None:
                return False, 'Error: Unable to get {name} Authentication, check your config!'.format(name=self.name)
            else:
                return True, 'Success: Connected and Authenticated'

    def pause_torrent(self, info_hash):
        """Get torrent and pause."""
        log.info('Pausing {client} torrent {hash} status.', {'client': self.name, 'hash': info_hash})
        if not self._get_auth():
            return False

        torrent = self.auth.find_torrent(info_hash.upper())

        if not torrent:
            log.debug('Could not locate torrent with {hash} status.', {'hash': info_hash})
            return

        return torrent.pause()

    def remove_torrent(self, info_hash):
        """Get torrent and remove."""
        log.info('Removing {client} torrent {hash} status.', {'client': self.name, 'hash': info_hash})
        if not self._get_auth():
            return False

        torrent = self.auth.find_torrent(info_hash.upper())

        if not torrent:
            log.debug('Could not locate torrent with {hash} status.', {'hash': info_hash})
            return

        return torrent.erase()

    def _torrent_properties(self, info_hash):
        """Get torrent properties."""
        log.debug('Get {client} torrent hash {hash} properties.', {'client': self.name, 'hash': info_hash})
        if not self._get_auth():
            return False

        torrent = self.auth.find_torrent(info_hash.upper())

        if not torrent:
            log.debug('Could not locate torrent with {hash} status.', {'hash': info_hash})
            return

        return torrent

    def torrent_completed(self, info_hash):
        """Check if torrent has finished downloading."""
        get_status = self.get_status(info_hash)
        if not get_status:
            return False

        return str(get_status) == 'Completed'

    def torrent_ratio(self, info_hash):
        """Get torrent ratio."""
        get_status = self.get_status(info_hash)
        if not get_status:
            return False

        return get_status.ratio

    def torrent_progress(self, info_hash):
        """Get torrent download progress."""
        get_status = self.get_status(info_hash)
        if not get_status:
            return False

        return get_status.progress

    def get_status(self, info_hash):
        """
        Return torrent status.

        Status codes:
        ```
            complete: 'Completed download'
            is_finished: 'Finished seeding (ratio reeched)'

        ```
        """
        torrent = self._torrent_properties(info_hash)
        if not torrent:
            return

        client_status = ClientStatus()
        if torrent.started:
            client_status.set_status_string('Downloading')

        if torrent.paused:
            client_status.set_status_string('Paused')

        # # if torrent['status'] == ?:
        # #     client_status.set_status_string('Failed')

        if torrent.complete:
            client_status.set_status_string('Completed')

        # Store ratio
        client_status.ratio = torrent.ratio

        # Store progress
        if torrent.bytes_done:
            client_status.progress = int(torrent.completed_bytes / torrent.bytes_done * 100)

        # Store destination
        client_status.destination = torrent.directory

        # Store resource
        client_status.resource = torrent.base_filename

        return client_status
示例#21
0
class RTorrentAPI(GenericClient):
    """rTorrent API class."""

    def __init__(self, host=None, username=None, password=None):
        """Constructor.

        :param host:
        :type host: string
        :param username:
        :type username: string
        :param password:
        :type password: string
        """
        super(RTorrentAPI, self).__init__('rTorrent', host, username, password)

    def _get_auth(self):
        if self.auth is not None:
            return self.auth

        if not self.host:
            return

        tp_kwargs = {}
        if app.TORRENT_AUTH_TYPE != 'none':
            tp_kwargs['authtype'] = app.TORRENT_AUTH_TYPE

        if not app.TORRENT_VERIFY_CERT:
            tp_kwargs['check_ssl_cert'] = False

        if self.username and self.password:
            self.auth = RTorrent(self.host, self.username, self.password, True, tp_kwargs=tp_kwargs)
        else:
            self.auth = RTorrent(self.host, None, None, True)

        return self.auth

    @staticmethod
    def _get_params(result):
        params = []

        # Set label
        label = app.TORRENT_LABEL
        if result.show.is_anime:
            label = app.TORRENT_LABEL_ANIME
        if label:
            params.append('d.custom1.set={0}'.format(label))

        if app.TORRENT_PATH:
            params.append('d.directory.set={0}'.format(app.TORRENT_PATH))

        return params

    def _add_torrent_uri(self, result):

        if not (self.auth or result):
            return False

        try:
            params = self._get_params(result)
            # Send magnet to rTorrent and start it
            torrent = self.auth.load_magnet(result.url, result.hash, start=True, params=params)
            if not torrent:
                return False

        except Exception as msg:
            log.warning('Error while sending torrent: {error!r}',
                        {'error': msg})
            return False
        else:
            return True

    def _add_torrent_file(self, result):

        if not (self.auth or result):
            return False

        try:
            params = self._get_params(result)
            # Send torrent to rTorrent and start it
            torrent = self.auth.load_torrent(result.content, start=True, params=params)
            if not torrent:
                return False

        except Exception as msg:
            log.warning('Error while sending torrent: {error!r}',
                        {'error': msg})
            return False
        else:
            return True

    def test_authentication(self):
        """Test connection using authentication.

        :return:
        :rtype: tuple(bool, str)
        """
        try:
            self.auth = None
            self._get_auth()
        except Exception:  # pylint: disable=broad-except
            return False, 'Error: Unable to connect to {name}'.format(name=self.name)
        else:
            if self.auth is None:
                return False, 'Error: Unable to get {name} Authentication, check your config!'.format(name=self.name)
            else:
                return True, 'Success: Connected and Authenticated'
示例#22
0
class TestRTorrentAPI(unittest.TestCase):
    def setUp(self):
        self.rt = RTorrent()
        self.test_file = "tests/tester.torrent"
        self.test_magnet = "magnet:?xt=urn:btih:f2ed240912dc324d6a30de6811a8747f80b9722d&dn=The+Wolverine+2013+DVDRip+x264+AC3-EVO&tr=udp%3A%2F%2Ftracker.openbittorrent.com%3A80&tr=udp%3A%2F%2Ftracker.publicbt.com%3A80&tr=udp%3A%2F%2Ftracker.istole.it%3A6969&tr=udp%3A%2F%2Ftracker.ccc.de%3A80&tr=udp%3A%2F%2Fopen.demonii.com%3A1337"

        files = self.rt.get_active_infohashes()
        for f in files:
            self.rt.erase(f)

    def test_add_torrent(self):
        self.rt.add_torrent_magnet(self.test_magnet)

        res = self.rt.get_active_infohashes()
        assert (len(res) == 1)

        self.rt.erase(res[0])

        res = self.rt.get_active_infohashes()
        assert (len(res) == 0)

    def test_torrent_size(self):
        self.rt.add_torrent_magnet(self.test_magnet)

        res = self.rt.get_active_infohashes()
        assert (len(res) == 1)

        time.sleep(10)

        assert (1500000000 < self.rt.get_size_bytes(res[0]) < 1600000000)

    def test_torrent_file_add(self):
        f = open(self.test_file)
        out = f.read()
        self.rt.add_torrent_file(out)

        res = self.rt.get_active_infohashes()
        assert (len(res) == 1)
示例#23
0
class Client(GenericClient):
    def __init__(self, host=None, username=None, password=None):
        super().__init__('rTorrent', host, username, password)

    def _get_auth(self):

        if self.auth is not None:
            return self.auth

        if not self.host:
            return

        self.host = self.host.rstrip('/')

        tp_kwargs = {}
        if settings.TORRENT_AUTH_TYPE and settings.TORRENT_AUTH_TYPE.lower(
        ) != 'none':
            tp_kwargs['authtype'] = settings.TORRENT_AUTH_TYPE

        if not settings.TORRENT_VERIFY_CERT:
            tp_kwargs['check_ssl_cert'] = False

        if self.username and self.password:
            self.auth = RTorrent(self.host,
                                 self.username,
                                 self.password,
                                 True,
                                 tp_kwargs=tp_kwargs or None)
        else:
            self.auth = RTorrent(self.host,
                                 None,
                                 None,
                                 True,
                                 tp_kwargs=tp_kwargs or None)

        return self.auth

    def _add_torrent_uri(self, result):

        if not (self.auth and result):
            return False

        try:
            # Send torrent magnet with params to rTorrent and optionally start download
            torrent = self.auth.load_magnet(result.url,
                                            result.hash,
                                            start=not settings.TORRENT_PAUSED,
                                            params=self._get_params(result))

            if not torrent:
                return False

            return True

        except Exception as error:
            logger.warning(
                _('Error while sending torrent: {error}'.format(error=error)))
            return False

    def _add_torrent_file(self, result):

        if not (self.auth and result):
            return False

        try:
            # Send torrent file with params to rTorrent and optionally start download
            torrent = self.auth.load_torrent(result.content,
                                             start=not settings.TORRENT_PAUSED,
                                             params=self._get_params(result))

            if not torrent:
                return False

            return True

        except Exception as error:
            logger.info(traceback.format_exc())
            logger.warning(
                _('Error while sending torrent: {error}'.format(error=error)))
            return False

    def testAuthentication(self):
        try:
            self._get_auth()

            if self.auth is not None:
                return True, _('Success: Connected and Authenticated')
            else:
                return False, _(
                    'Error: Unable to get {self.name} Authentication, check your config!'
                )
        except Exception as error:
            return False, _(
                'Error: Unable to connect to {name}: {error}'.format(
                    name=self.name, error=error))

    @staticmethod
    def _get_params(result):
        params = []

        # Set label
        label = settings.TORRENT_LABEL
        if result.show.is_anime:
            label = settings.TORRENT_LABEL_ANIME
        if label:
            params.append(f'd.custom1.set={label}')

        # Set download folder
        if settings.TORRENT_PATH:
            params.append(f'd.directory.set={settings.TORRENT_PATH}')

        return params
示例#24
0
class rTorrent(Downloader):

    protocol = ['torrent', 'torrent_magnet']
    rt = None

    def connect(self):
        # Already connected?
        if self.rt is not None:
            return self.rt

        # Ensure url is set
        if not self.conf('url'):
            log.error('Config properties are not filled in correctly, url is missing.')
            return False

        if self.conf('username') and self.conf('password'):
            self.rt = RTorrent(
                self.conf('url'),
                self.conf('username'),
                self.conf('password')
            )
        else:
            self.rt = RTorrent(self.conf('url'))

        return self.rt

    def _update_provider_group(self, name, data):
        if data.get('seed_time'):
            log.info('seeding time ignored, not supported')

        if not name:
            return False

        if not self.connect():
            return False

        views = self.rt.get_views()

        if name not in views:
            self.rt.create_group(name)

        group = self.rt.get_group(name)

        try:
            if data.get('seed_ratio'):
                ratio = int(float(data.get('seed_ratio')) * 100)
                log.debug('Updating provider ratio to %s, group name: %s', (ratio, name))

                # Explicitly set all group options to ensure it is setup correctly
                group.set_upload('1M')
                group.set_min(ratio)
                group.set_max(ratio)
                group.set_command('d.stop')
                group.enable()
            else:
                # Reset group action and disable it
                group.set_command()
                group.disable()
        except MethodError, err:
            log.error('Unable to set group options: %s', err.msg)
            return False

        return True