コード例 #1
0
ファイル: transmission.py プロジェクト: QifanYang-bw/AutoPT
class TransmissionClient(BTClientBase):
    def __init__(self,
                 rpc_address,
                 rpc_port,
                 username,
                 password,
                 config={'path': '/transmission/rpc'}):
        """
        Transmission Client for AutoPT

        config: dict
            {path: '/transmission/'}
        """

        self.rpc_address = rpc_address
        self.rpc_port = int(rpc_port)
        self.username = username
        self.password = password
        self.path = config['path']  # For now, only consider path

        self.connected = False
        self.client = None

    def connect(self):
        try:
            self.client = Client(host=self.rpc_address,
                                 port=self.rpc_port,
                                 username=self.username,
                                 password=self.password,
                                 path=self.path)
            self.connected = True
            ret = ClientRet(ret_type=2)
        except:
            ret = ClientRet(ret_type=-2)
        finally:
            return ret

    def add_torrent(self, torrent_path, download_path=None):
        if not self.connected:
            ret = ClientRet(ret_type=-2)
            return ret

        abs_torrent_path = str(Path(torrent_path).resolve())
        try:
            if download_path is None:
                torrent_obj = self.client.add_torrent(abs_torrent_path,
                                                      paused=False)
            else:
                download_path = str(Path(download_path).resolve())
                torrent_obj = self.client.add_torrent(
                    abs_torrent_path, download_dir=download_path,
                    paused=False)  # Must be absolute path
            if torrent_obj is not None:
                ret = ClientRet(ret_type=3, ret_value=torrent_obj.hashString)
        except:
            ret = ClientRet(ret_type=-3)
        finally:
            return ret

    def list_torrents(self):
        if not self.connected:
            ret = ClientRet(ret_type=-2)
            return ret

        try:
            torrent_obj_list = self.client.get_torrents()
            session_status = {}
            for torrent_obj in torrent_obj_list:
                is_finished = math.isclose(torrent_obj.progress, 100)
                torrent_status = TorrentStatus(
                    torrent_id=torrent_obj.hashString,
                    is_finished=is_finished,
                    name=torrent_obj.name)

                session_status[torrent_obj.hashString] = torrent_status

            ret = ClientRet(ret_type=4, ret_value=session_status)
        except:
            ret = ClientRet(ret_type=-4)
        finally:
            return ret

    def get_torrent_status(self, idx):
        if not self.connected:
            ret = ClientRet(ret_type=-2)
            return ret

        try:
            torrent_obj = self.client.get_torrent(idx)
            is_finished = math.isclose(torrent_obj.progress, 100)
            torrent_status = TorrentStatus(torrent_id=torrent_obj.hashString,
                                           is_finished=is_finished,
                                           name=torrent_obj.name)

            ret = ClientRet(ret_type=6, ret_value=torrent_status)
        except:
            ret = ClientRet(ret_type=-6)
        finally:
            return ret

    def del_torrent(self, idx, remove_data=True):
        if not self.connected:
            ret = ClientRet(ret_type=-2)
            return ret

        try:
            torrent_obj = self.client.get_torrent(idx)
            torrent_exist = True
        except:
            torrent_exist = False

        if torrent_exist:
            try:
                self.client.remove_torrent(idx, delete_data=remove_data)
                ret = ClientRet(ret_type=5)
            except:
                ret = ClientRet(ret_type=-5)
            finally:
                return ret
        else:
            ret = ClientRet(ret_type=-5)
            return ret

    def disconnect(self):
        self.connected = False
        self.client = None
        ret = ClientRet(ret_type=0)
        return ret
コード例 #2
0
class TorrentService:
    def __init__(self, address, port, username, password):
        self.client = Client(address=address,
                             port=port,
                             username=username,
                             password=password)

    def torrent_icon(self, status):
        if status == 'stopped':
            return '⏹'
        else:
            return '▶️'

    def torrent_description(self, torrent):
        return '{} {} {:01.0f}%, {}'.format(self.torrent_icon(torrent.status),
                                            torrent.status, torrent.progress,
                                            torrent.name)

    def add_torrent(self, url, path):
        tor = self.client.add_torrent(url, download_dir=path)
        return tor.name

    def torrent_list(self, update, context):
        torrents = self.client.get_torrents()
        torrents = sorted(torrents, key=lambda i: i.queue_position)
        keyboard = []
        for torrent in torrents:
            tname = self.torrent_description(torrent)
            keyboard.append([
                InlineKeyboardButton(tname,
                                     callback_data='t_info_{}'.format(
                                         torrent.id))
            ])
        keyboard.append(
            [InlineKeyboardButton('✕ Cancel', callback_data='cancel')])
        reply_markup = InlineKeyboardMarkup(keyboard)
        update.message.reply_text(text='Torrents', reply_markup=reply_markup)
        return TORRENTLIST

    def torrent_info(self, update, context):
        query = update.callback_query
        tid = re.search('^t_info_(\d+?)$', query.data).group(1)
        tor = self.client.get_torrent(tid)
        stop_resume = []
        if tor.status == 'stopped':
            stop_resume = [
                InlineKeyboardButton('▷ Resume',
                                     callback_data='t_resume_{}'.format(tid)),
                InlineKeyboardButton(
                    '▶︎ Resume now',
                    callback_data='t_resume_now_{}'.format(tid))
            ]
        elif tor.status == 'download pending':
            stop_resume = [
                InlineKeyboardButton(
                    '▶︎ Resume now',
                    callback_data='t_resume_now_{}'.format(tid)),
                InlineKeyboardButton('◼︎ Stop',
                                     callback_data='t_stop_{}'.format(tid))
            ]
        else:
            stop_resume = [
                InlineKeyboardButton('◼︎ Stop',
                                     callback_data='t_stop_{}'.format(tid))
            ]
        keyboard = [[
            InlineKeyboardButton('▲ Top',
                                 callback_data='t_top_{}'.format(tid)),
            InlineKeyboardButton('△ Up', callback_data='t_up_{}'.format(tid)),
            InlineKeyboardButton('▽ Down',
                                 callback_data='t_down_{}'.format(tid)),
            InlineKeyboardButton('▼ Bottom',
                                 callback_data='t_bottom_{}'.format(tid))
        ], stop_resume,
                    [
                        InlineKeyboardButton(
                            '◉ Verify',
                            callback_data='t_verify_{}'.format(tid))
                    ],
                    [
                        InlineKeyboardButton(
                            '☒ Remove',
                            callback_data='t_remove_confirm_{}'.format(tid))
                    ],
                    [InlineKeyboardButton('✕ Cancel', callback_data='cancel')]]
        reply_markup = InlineKeyboardMarkup(keyboard)
        bot = context.bot
        bot.edit_message_text(chat_id=query.message.chat_id,
                              message_id=query.message.message_id,
                              reply_markup=reply_markup,
                              text=self.torrent_description(tor))

        return TORRENTINFO

    def move_up(self, update, context):
        query = update.callback_query
        tid = re.search('^t_up_(\d+?)$', query.data).group(1)
        self.client.queue_up(tid)
        tor = self.client.get_torrent(tid)
        context.bot.edit_message_text(chat_id=query.message.chat_id,
                                      message_id=query.message.message_id,
                                      text='{} has been moved up'.format(
                                          tor.name))
        return ConversationHandler.END

    def move_down(self, update, context):
        query = update.callback_query
        tid = re.search('^t_down_(\d+?)$', query.data).group(1)
        self.client.queue_down(tid)
        tor = self.client.get_torrent(tid)
        context.bot.edit_message_text(chat_id=query.message.chat_id,
                                      message_id=query.message.message_id,
                                      text='{} has been moved down'.format(
                                          tor.name))
        return ConversationHandler.END

    def move_top(self, update, context):
        query = update.callback_query
        tid = re.search('^t_top_(\d+?)$', query.data).group(1)
        self.client.queue_top(tid)
        tor = self.client.get_torrent(tid)
        context.bot.edit_message_text(chat_id=query.message.chat_id,
                                      message_id=query.message.message_id,
                                      text='{} has been moved to top'.format(
                                          tor.name))
        return ConversationHandler.END

    def move_bottom(self, update, context):
        query = update.callback_query
        tid = re.search('^t_bottom_(\d+?)$', query.data).group(1)
        self.client.queue_bottom(tid)
        tor = self.client.get_torrent(tid)
        context.bot.edit_message_text(
            chat_id=query.message.chat_id,
            message_id=query.message.message_id,
            text='{} has been moved to bottom'.format(tor.name))
        return ConversationHandler.END

    def stop_torrent(self, update, context):
        tid = re.search('^t_stop_(\d+?)$', update.callback_query.data).group(1)
        tor = self.client.get_torrent(tid)
        self.client.stop_torrent(tid)
        context.bot.edit_message_text(
            chat_id=update.callback_query.message.chat_id,
            message_id=update.callback_query.message.message_id,
            text='{} has been stopped'.format(tor.name))
        return ConversationHandler.END

    def resume_torrent(self, update, context):
        tid = re.search('^t_resume_(\d+?)$',
                        update.callback_query.data).group(1)
        tor = self.client.get_torrent(tid)
        self.client.start_torrent(tid)
        context.bot.edit_message_text(
            chat_id=update.callback_query.message.chat_id,
            message_id=update.callback_query.message.message_id,
            text='{} has been resumed'.format(tor.name))
        return ConversationHandler.END

    def resume_torrent_now(self, update, context):
        tid = re.search('^t_resume_now_(\d+?)$',
                        update.callback_query.data).group(1)
        tor = self.client.get_torrent(tid)
        self.client.start_torrent(tid, bypass_queue=True)
        context.bot.edit_message_text(
            chat_id=update.callback_query.message.chat_id,
            message_id=update.callback_query.message.message_id,
            text='{} has been resumed'.format(tor.name))
        return ConversationHandler.END

    def verify_torrent(self, update, context):
        tid = re.search('^t_verify_(\d+?)$',
                        update.callback_query.data).group(1)
        tor = self.client.get_torrent(tid)
        self.client.verify_torrent(tid)
        context.bot.edit_message_text(
            chat_id=update.callback_query.message.chat_id,
            message_id=update.callback_query.message.message_id,
            text='{} verification has been started'.format(tor.name))
        return ConversationHandler.END

    def remove_torrent_confirm(self, update, context):
        tid = re.search('^t_remove_confirm_(\d+?)$',
                        update.callback_query.data).group(1)
        tor = self.client.get_torrent(tid)
        keyboard = [[
            InlineKeyboardButton('☒ Remove torrent',
                                 callback_data='t_remove_{}'.format(tid))
        ],
                    [
                        InlineKeyboardButton(
                            '⌧ Remove torrent and delete files',
                            callback_data='t_remove_complete_{}'.format(tid))
                    ],
                    [InlineKeyboardButton('✕ Cancel', callback_data='cancel')]]
        reply_markup = InlineKeyboardMarkup(keyboard)
        context.bot.edit_message_text(
            chat_id=update.callback_query.message.chat_id,
            message_id=update.callback_query.message.message_id,
            reply_markup=reply_markup,
            text='Are you sure you want to remove {}?'.format(tor.name))
        return REMOVETORRENT

    def remove_torrent(self, update, context):
        tid = re.search('^t_remove_(\d+?)$',
                        update.callback_query.data).group(1)
        tor = self.client.get_torrent(tid)
        self.client.remove_torrent(tid)
        context.bot.edit_message_text(
            chat_id=update.callback_query.message.chat_id,
            message_id=update.callback_query.message.message_id,
            text='{} has been removed'.format(tor.name))
        return ConversationHandler.END

    def remove_torrent_complete(self, update, context):
        tid = re.search('^t_remove_complete_(\d+?)$',
                        update.callback_query.data).group(1)
        tor = self.client.get_torrent(tid)
        self.client.remove_torrent(tid, delete_data=True)
        context.bot.edit_message_text(
            chat_id=update.callback_query.message.chat_id,
            message_id=update.callback_query.message.message_id,
            text='{} has been removed'.format(tor.name))
        return ConversationHandler.END
コード例 #3
0
class TransmissionPrioritizer(object):

    def __init__(self, host, port, user, passwd):
        self._host = host
        self._port = port
        self._user = user
        self._pass = passwd
        logger.debug(
            'Connecting to Transmission at %s:%s as user %s',
            host, port, user
        )
        self._client = Client(
            address=host, port=port, user=user, password=passwd
        )
        logger.debug('Connected to Transmission')

    def run(self, batch=2, rm_finished=False):
        logger.debug('Getting current torrents...')
        torrents = self._get_active_torrents()
        logger.info('Found %d active torrent(s)...', len(torrents))
        for t in torrents:
            self._set_file_priority(t, batch)
        logger.info('Done.')
        if rm_finished:
            self._rm_finished_torrents()

    def _set_file_priority(self, torrent, batch):
        t_id = torrent._fields['id'].value
        logger.info(
            'Checking files in torrent %d (%s)', t_id,
            torrent._get_name_string()
        )
        files = self._client.get_files(ids=[t_id])[t_id]
        logger.debug('Torrent has %d files', len(files))
        incomplete = []
        for _id in sorted(files.keys(), key=lambda x: files[x]['name']):
            pct = (files[_id]['completed'] / files[_id]['size']) * 100
            logger.debug(
                'File %d: %s - %.2f%% complete - %s, priority %s', _id,
                files[_id]['name'], pct,
                'selected' if files[_id]['selected'] else 'unselected',
                files[_id]['priority']
            )
            if pct < 100:
                incomplete.append(_id)
        logger.debug('%d files in torrent are incomplete', len(incomplete))
        if len(incomplete) > batch:
            selected = incomplete[:batch]
        else:
            selected = incomplete
        logger.debug('First %d incomplete files: %s', len(selected), selected)
        data = {t_id: {}}
        for _id in files:
            data[t_id][_id] = {
                'selected': files[_id]['selected'],
                'priority': 'high' if _id in selected else 'normal'
            }
        logger.info(
            'Ensuring high priority on first %d incomplete files: %s',
            len(selected), ', '.join([
                '%d (%s)' % (x, files[x]['name']) for x in selected
            ])
        )
        logger.debug('set_files: %s', data)
        self._client.set_files(data)

    def _get_active_torrents(self):
        r = self._client.get_torrents()
        active = []
        for t in r:
            logger.debug(
                'Torrent %s (%s) - %s, %.2f%% complete',
                t._fields['id'].value, t._get_name_string(),
                t.status, t.progress
            )
            if t.status in ['downloading', 'download pending']:
                active.append(t)
        logger.debug('%d of %d torrents active', len(active), len(r))
        return active

    def _rm_finished_torrents(self):
        logger.debug('Looking for finished torrents to remove...')
        r = self._client.get_torrents()
        active = []
        for t in r:
            logger.debug(
                'Torrent %s (%s) - %s, %.2f%% complete',
                t._fields['id'].value, t._get_name_string(),
                t.status, t.progress
            )
            if t.status != 'seeding' or t.progress != 100:
                continue
            logger.info(
                'Removing finished/seeding torrent: %s (%s)',
                t._fields['id'].value, t._get_name_string()
            )
            self._client.remove_torrent(t._fields['id'].value)
コード例 #4
0
class TransmissionPrioritizer(object):
    def __init__(self, host, port, user, passwd):
        self._host = host
        self._port = port
        self._user = user
        self._pass = passwd
        logger.debug('Connecting to Transmission at %s:%s as user %s', host,
                     port, user)
        self._client = Client(address=host,
                              port=port,
                              user=user,
                              password=passwd)
        logger.debug('Connected to Transmission')

    def run(self, batch=2, rm_finished=False):
        logger.debug('Getting current torrents...')
        torrents = self._get_active_torrents()
        logger.info('Found %d active torrent(s)...', len(torrents))
        for t in torrents:
            self._set_file_priority(t, batch)
        logger.info('Done.')
        if rm_finished:
            self._rm_finished_torrents()

    def _set_file_priority(self, torrent, batch):
        t_id = torrent._fields['id'].value
        logger.info('Checking files in torrent %d (%s)', t_id,
                    torrent._get_name_string())
        files = self._client.get_files(ids=[t_id])[t_id]
        logger.debug('Torrent has %d files', len(files))
        incomplete = []
        for _id in sorted(files.keys(), key=lambda x: files[x]['name']):
            pct = (files[_id]['completed'] / files[_id]['size']) * 100
            logger.debug(
                'File %d: %s - %.2f%% complete - %s, priority %s', _id,
                files[_id]['name'], pct,
                'selected' if files[_id]['selected'] else 'unselected',
                files[_id]['priority'])
            if pct < 100:
                incomplete.append(_id)
        logger.debug('%d files in torrent are incomplete', len(incomplete))
        if len(incomplete) > batch:
            selected = incomplete[:batch]
        else:
            selected = incomplete
        logger.debug('First %d incomplete files: %s', len(selected), selected)
        data = {t_id: {}}
        for _id in files:
            data[t_id][_id] = {
                'selected': files[_id]['selected'],
                'priority': 'high' if _id in selected else 'normal'
            }
        logger.info(
            'Ensuring high priority on first %d incomplete files: %s',
            len(selected),
            ', '.join(['%d (%s)' % (x, files[x]['name']) for x in selected]))
        logger.debug('set_files: %s', data)
        self._client.set_files(data)

    def _get_active_torrents(self):
        r = self._client.get_torrents()
        active = []
        for t in r:
            logger.debug('Torrent %s (%s) - %s, %.2f%% complete',
                         t._fields['id'].value, t._get_name_string(), t.status,
                         t.progress)
            if t.status in ['downloading', 'download pending']:
                active.append(t)
        logger.debug('%d of %d torrents active', len(active), len(r))
        return active

    def _rm_finished_torrents(self):
        logger.debug('Looking for finished torrents to remove...')
        r = self._client.get_torrents()
        active = []
        for t in r:
            logger.debug('Torrent %s (%s) - %s, %.2f%% complete',
                         t._fields['id'].value, t._get_name_string(), t.status,
                         t.progress)
            if t.status != 'seeding' or t.progress != 100:
                continue
            logger.info('Removing finished/seeding torrent: %s (%s)',
                        t._fields['id'].value, t._get_name_string())
            self._client.remove_torrent(t._fields['id'].value)
コード例 #5
0
class TransmissionRpcClient(TorrentClient):
    def __init__(self, url: str, port: int, user: str, password: str):
        super().__init__(url, port, user, password)
        parse = urlparse(url)

        self.client_args = {
            "protocol": parse.scheme,
            "host": parse.netloc,
            "port": port,
            "path": parse.path,
            "username": user,
            "password": password,
        }

        # print(self.client_args)
        self.session: Client = None

    def connect(self):
        self.session = Client(**self.client_args)

    def disconnect(self):
        pass

    def add_torrent(self, file: bytes, paused: bool, path: str = None) -> str:

        if self.session is None:
            raise ConnectionError()

        send = BytesIO(file)

        args = {
            "torrent": send,
            "paused": paused,
        }

        if path is not None:
            args["download_dir"] = path

        torrent = self.session.add_torrent(**args)
        return torrent.id

    def del_torrent(self, torrent_id: str):
        self.session.remove_torrent(ids=torrent_id, delete_data=True)

    def get_torrent_status(self, torrent_id: str) -> TorrentStatus:
        torrent = self.session.get_torrent(torrent_id=torrent_id)
        return TransmissionRpcStatus(torrent, self.session)

    def get_torrents_status(self,
                            torrent_id: List[str]) -> List[TorrentStatus]:
        torrents = self.session.get_torrents(ids=torrent_id)
        ret: List[TorrentStatus] = list()
        for torrent in torrents:
            ret.append(TransmissionRpcStatus(torrent, self.session))

        return ret

    def start_torrent(self, torrent_id: str):
        torrent = self.session.get_torrent(torrent_id=torrent_id)
        torrent.start()

    def stop_torrent(self, torrent_id: str):
        torrent = self.session.get_torrent(torrent_id=torrent_id)
        torrent.stop()