def __init__(self, uri, auth=None, verify_server=False, verify_ssl=True, sp=None, sp_kwargs=None): self.connection = Connection(uri, auth, verify_ssl, sp, sp_kwargs) self.torrents = [] # : List of L{Torrent} instances self._torrent_cache = [] # Verify connection is valid if verify_server is True: self.connection.verify()
class RTorrent: """ Create a new rTorrent connection """ rpc_prefix = None def __init__(self, uri, auth=None, verify_server=False, verify_ssl=True, sp=None, sp_kwargs=None): self.connection = Connection(uri, auth, verify_ssl, sp, sp_kwargs) self.torrents = [] # : List of L{Torrent} instances self._torrent_cache = [] # Verify connection is valid if verify_server is True: self.connection.verify() @property def client(self): return self.connection.client def _get_conn(self): return self.client def get_torrents(self, view="main"): """Get list of all torrents in specified view @return: list of L{Torrent} instances @rtype: list @todo: add validity check for specified view """ self.torrents = [] methods = rtorrent.torrent.methods retriever_methods = [m for m in methods if m.is_retriever() and m.is_available(self)] m = rtorrent.rpc.Multicall(self) m.add("d.multicall", view, "d.get_hash=", *[method.rpc_call + "=" for method in retriever_methods]) results = m.call()[0] # only sent one call, only need first result for result in results: results_dict = {} # build results_dict for m, r in zip(retriever_methods, result[1:]): # result[0] is the info_hash results_dict[m.varname] = rtorrent.rpc.process_result(m, r) self.torrents.append( Torrent(self, info_hash=result[0], **results_dict) ) self._manage_torrent_cache() return(self.torrents) def _manage_torrent_cache(self): """Carry tracker/peer/file lists over to new torrent list""" for torrent in self._torrent_cache: new_torrent = rtorrent.common.find_torrent(torrent.info_hash, self.torrents) if new_torrent is not None: new_torrent.files = torrent.files new_torrent.peers = torrent.peers new_torrent.trackers = torrent.trackers self._torrent_cache = self.torrents def _get_load_function(self, file_type, start, verbose): """Determine correct "load torrent" RPC method""" func_name = None if file_type == "url": # url strings can be input directly if start and verbose: func_name = "load_start_verbose" elif start: func_name = "load_start" elif verbose: func_name = "load_verbose" else: func_name = "load" elif file_type in ["file", "raw"]: if start and verbose: func_name = "load_raw_start_verbose" elif start: func_name = "load_raw_start" elif verbose: func_name = "load_raw_verbose" else: func_name = "load_raw" return(func_name) def load_torrent(self, torrent, start=False, verbose=False, verify_load=True, verify_retries=3): """ Loads torrent into rTorrent (with various enhancements) @param torrent: can be a url, a path to a local file, or the raw data of a torrent file @type torrent: str @param start: start torrent when loaded @type start: bool @param verbose: print error messages to rTorrent log @type verbose: bool @param verify_load: verify that torrent was added to rTorrent successfully @type verify_load: bool @return: Depends on verify_load: - if verify_load is True, (and the torrent was loaded successfully), it'll return a L{Torrent} instance - if verify_load is False, it'll return None @rtype: L{Torrent} instance or None @raise AssertionError: If the torrent wasn't successfully added to rTorrent - Check L{TorrentParser} for the AssertionError's it raises @note: Because this function includes url verification (if a url was input) as well as verification as to whether the torrent was successfully added, this function doesn't execute instantaneously. If that's what you're looking for, use load_torrent_simple() instead. """ p = self._get_conn() tp = TorrentParser(torrent) torrent = xmlrpclib.Binary(tp._raw_torrent) info_hash = tp.info_hash func_name = self._get_load_function("raw", start, verbose) # load torrent getattr(p, func_name)(torrent) if verify_load: i = 0 while i < verify_retries: self.get_torrents() if info_hash in [t.info_hash for t in self.torrents]: break # was still getting AssertionErrors, delay should help time.sleep(1) i += 1 assert info_hash in [t.info_hash for t in self.torrents],\ "Adding torrent was unsuccessful." return(find_torrent(info_hash, self.torrents)) def load_torrent_simple(self, torrent, file_type, start=False, verbose=False): """Loads torrent into rTorrent @param torrent: can be a url, a path to a local file, or the raw data of a torrent file @type torrent: str @param file_type: valid options: "url", "file", or "raw" @type file_type: str @param start: start torrent when loaded @type start: bool @param verbose: print error messages to rTorrent log @type verbose: bool @return: None @raise AssertionError: if incorrect file_type is specified @note: This function was written for speed, it includes no enhancements. If you input a url, it won't check if it's valid. You also can't get verification that the torrent was successfully added to rTorrent. Use load_torrent() if you would like these features. """ p = self._get_conn() assert file_type in ["raw", "file", "url"], \ "Invalid file_type, options are: 'url', 'file', 'raw'." func_name = self._get_load_function(file_type, start, verbose) if file_type == "file": # since we have to assume we're connected to a remote rTorrent # client, we have to read the file and send it to rT as raw assert os.path.isfile(torrent), \ "Invalid path: \"{0}\"".format(torrent) torrent = open(torrent, "rb").read() if file_type in ["raw", "file"]: finput = xmlrpclib.Binary(torrent) elif file_type == "url": finput = torrent getattr(p, func_name)(finput) def get_views(self): p = self._get_conn() return p.view_list() def create_group(self, name, persistent=True, view=None): p = self._get_conn() if persistent is True: p.group.insert_persistent_view('', name) else: assert view is not None, "view parameter required on non-persistent groups" p.group.insert('', name, view) self.connection._update_rpc_methods() def get_group(self, name): assert name is not None, "group name required" group = Group(self, name) group.update() return group def set_dht_port(self, port): """Set DHT port @param port: port @type port: int @raise AssertionError: if invalid port is given """ assert is_valid_port(port), "Valid port range is 0-65535" self.dht_port = self._p.set_dht_port(port) def enable_check_hash(self): """Alias for set_check_hash(True)""" self.set_check_hash(True) def disable_check_hash(self): """Alias for set_check_hash(False)""" self.set_check_hash(False) def find_torrent(self, info_hash): """Frontend for rtorrent.common.find_torrent""" return(rtorrent.common.find_torrent(info_hash, self.get_torrents())) def poll(self): """ poll rTorrent to get latest torrent/peer/tracker/file information @note: This essentially refreshes every aspect of the rTorrent connection, so it can be very slow if working with a remote connection that has a lot of torrents loaded. @return: None """ self.update() torrents = self.get_torrents() for t in torrents: t.poll() def update(self): """Refresh rTorrent client info @note: All fields are stored as attributes to self. @return: None """ multicall = rtorrent.rpc.Multicall(self) retriever_methods = [m for m in methods if m.is_retriever() and m.is_available(self)] for method in retriever_methods: multicall.add(method) multicall.call()
class RTorrent: """ Create a new rTorrent connection """ rpc_prefix = None def __init__(self, uri, auth=None, verify_server=False, verify_ssl=True, sp=None, sp_kwargs=None): self.connection = Connection(uri, auth, verify_ssl, sp, sp_kwargs) self.torrents = [] # : List of L{Torrent} instances self._torrent_cache = [] # Verify connection is valid if verify_server is True: self.connection.verify() @property def client(self): return self.connection.client def _get_conn(self): return self.client def get_torrents(self, view="main"): """Get list of all torrents in specified view @return: list of L{Torrent} instances @rtype: list @todo: add validity check for specified view """ self.torrents = [] methods = rtorrent.torrent.methods retriever_methods = [m for m in methods if m.is_retriever() and m.is_available(self)] m = rtorrent.rpc.Multicall(self) m.add("d.multicall", view, "d.get_hash=", *[method.rpc_call + "=" for method in retriever_methods]) results = m.call()[0] # only sent one call, only need first result for result in results: results_dict = {} # build results_dict for m, r in zip(retriever_methods, result[1:]): # result[0] is the info_hash results_dict[m.varname] = rtorrent.rpc.process_result(m, r) self.torrents.append( Torrent(self, info_hash=result[0], **results_dict) ) self._manage_torrent_cache() return(self.torrents) def _manage_torrent_cache(self): """Carry tracker/peer/file lists over to new torrent list""" for torrent in self._torrent_cache: new_torrent = rtorrent.common.find_torrent(torrent.info_hash, self.torrents) if new_torrent is not None: new_torrent.files = torrent.files new_torrent.peers = torrent.peers new_torrent.trackers = torrent.trackers self._torrent_cache = self.torrents def _get_load_function(self, file_type, start, verbose): """Determine correct "load torrent" RPC method""" func_name = None if file_type == "url": # url strings can be input directly if start and verbose: func_name = "load.start_verbose" elif start: func_name = "load.start" elif verbose: func_name = "load.verbose" else: func_name = "load" elif file_type in ["file", "raw"]: if start and verbose: func_name = "load.raw_start_verbose" elif start: func_name = "load.raw_start" elif verbose: func_name = "load.raw_verbose" else: func_name = "load.raw" return(func_name) def load_magnet(self, magneturl, info_hash, start=False, verbose=False, verify_load=True, verify_retries=3): p = self._get_conn() info_hash = info_hash.upper() func_name = self._get_load_function("url", start, verbose) # load magnet getattr(p, func_name)(magneturl) if verify_load: i = 0 while i < verify_retries: for torrent in self.get_torrents(): if torrent.info_hash != info_hash: continue time.sleep(1) i += 1 # Resolve magnet to torrent torrent.start() assert info_hash in [t.info_hash for t in self.torrents],\ "Adding magnet was unsuccessful." i = 0 while i < verify_retries: for torrent in self.get_torrents(): if torrent.info_hash == info_hash: if str(info_hash) not in str(torrent.name): time.sleep(1) i += 1 return(torrent) def load_torrent(self, torrent, start=False, verbose=False, verify_load=True, verify_retries=3): """ Loads torrent into rTorrent (with various enhancements) @param torrent: can be a url, a path to a local file, or the raw data of a torrent file @type torrent: str @param start: start torrent when loaded @type start: bool @param verbose: print error messages to rTorrent log @type verbose: bool @param verify_load: verify that torrent was added to rTorrent successfully @type verify_load: bool @return: Depends on verify_load: - if verify_load is True, (and the torrent was loaded successfully), it'll return a L{Torrent} instance - if verify_load is False, it'll return None @rtype: L{Torrent} instance or None @raise AssertionError: If the torrent wasn't successfully added to rTorrent - Check L{TorrentParser} for the AssertionError's it raises @note: Because this function includes url verification (if a url was input) as well as verification as to whether the torrent was successfully added, this function doesn't execute instantaneously. If that's what you're looking for, use load_torrent_simple() instead. """ p = self._get_conn() tp = TorrentParser(torrent) torrent = xmlrpclib.Binary(tp._raw_torrent) info_hash = tp.info_hash func_name = self._get_load_function("raw", start, verbose) # load torrent # rtorrent > 0.9.6 requires first parameter @target target = "" getattr(p, func_name)(target, torrent) if verify_load: i = 0 while i < verify_retries: self.get_torrents() if info_hash in [t.info_hash for t in self.torrents]: break # was still getting AssertionErrors, delay should help time.sleep(1) i += 1 assert info_hash in [t.info_hash for t in self.torrents],\ "Adding torrent was unsuccessful." return(find_torrent(info_hash, self.torrents)) def load_torrent_simple(self, torrent, file_type, start=False, verbose=False): """Loads torrent into rTorrent @param torrent: can be a url, a path to a local file, or the raw data of a torrent file @type torrent: str @param file_type: valid options: "url", "file", or "raw" @type file_type: str @param start: start torrent when loaded @type start: bool @param verbose: print error messages to rTorrent log @type verbose: bool @return: None @raise AssertionError: if incorrect file_type is specified @note: This function was written for speed, it includes no enhancements. If you input a url, it won't check if it's valid. You also can't get verification that the torrent was successfully added to rTorrent. Use load_torrent() if you would like these features. """ p = self._get_conn() assert file_type in ["raw", "file", "url"], \ "Invalid file_type, options are: 'url', 'file', 'raw'." func_name = self._get_load_function(file_type, start, verbose) if file_type == "file": # since we have to assume we're connected to a remote rTorrent # client, we have to read the file and send it to rT as raw assert os.path.isfile(torrent), \ "Invalid path: \"{0}\"".format(torrent) torrent = open(torrent, "rb").read() if file_type in ["raw", "file"]: finput = xmlrpclib.Binary(torrent) elif file_type == "url": finput = torrent # rtorrent > 0.9.6 requires first parameter @target target = "" getattr(p, func_name)(target, finput) def get_views(self): p = self._get_conn() return p.view_list() def create_group(self, name, persistent=True, view=None): p = self._get_conn() if persistent is True: p.group.insert_persistent_view('', name) else: assert view is not None, "view parameter required on non-persistent groups" p.group.insert('', name, view) self.connection._update_rpc_methods() def get_group(self, name): assert name is not None, "group name required" group = Group(self, name) group.update() return group def set_dht_port(self, port): """Set DHT port @param port: port @type port: int @raise AssertionError: if invalid port is given """ assert is_valid_port(port), "Valid port range is 0-65535" self.dht_port = self._p.set_dht_port(port) def enable_check_hash(self): """Alias for set_check_hash(True)""" self.set_check_hash(True) def disable_check_hash(self): """Alias for set_check_hash(False)""" self.set_check_hash(False) def find_torrent(self, info_hash): """Frontend for rtorrent.common.find_torrent""" return(rtorrent.common.find_torrent(info_hash, self.get_torrents())) def poll(self): """ poll rTorrent to get latest torrent/peer/tracker/file information @note: This essentially refreshes every aspect of the rTorrent connection, so it can be very slow if working with a remote connection that has a lot of torrents loaded. @return: None """ self.update() torrents = self.get_torrents() for t in torrents: t.poll() def update(self): """Refresh rTorrent client info @note: All fields are stored as attributes to self. @return: None """ multicall = rtorrent.rpc.Multicall(self) retriever_methods = [m for m in methods if m.is_retriever() and m.is_available(self)] for method in retriever_methods: multicall.add(method) multicall.call()