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
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
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()