def test_on_metadata_received_alert(self): """ Testing whether the right operations happen when we receive metadata """ test_deferred = Deferred() def mocked_checkpoint(): test_deferred.callback(None) mocked_file = MockObject() mocked_file.path = 'test' self.libtorrent_download_impl.handle.trackers = lambda: [] self.libtorrent_download_impl.handle.save_resume_data = lambda: None torrent_dict = {'name': 'test', 'piece length': 42, 'pieces': '', 'files': []} get_info_from_handle(self.libtorrent_download_impl.handle).metadata = lambda: lt.bencode(torrent_dict) get_info_from_handle(self.libtorrent_download_impl.handle).files = lambda: [mocked_file] self.libtorrent_download_impl.checkpoint = mocked_checkpoint self.libtorrent_download_impl.session = MockObject() self.libtorrent_download_impl.session.lm = MockObject() self.libtorrent_download_impl.session.lm.rtorrent_handler = None self.libtorrent_download_impl.session.lm.torrent_db = None self.libtorrent_download_impl.handle.save_path = lambda: None self.libtorrent_download_impl.handle.prioritize_files = lambda _: None self.libtorrent_download_impl.get_save_path = lambda: '' self.libtorrent_download_impl.get_share_mode = lambda: False self.libtorrent_download_impl.on_metadata_received_alert(None) return test_deferred
def test_metadata_received_invalid_torrent_with_value_error(self): """ Testing whether the right operations happen when we receive metadata but the torrent info is invalid and throws Value Error """ def mocked_checkpoint(): raise RuntimeError("This code should not be reached!") mocked_file = MockObject() mocked_file.path = 'test' # The line below should trigger Value Error self.libtorrent_download_impl.handle.trackers = lambda: [{ 'url': 'no-DHT' }] torrent_dict = { 'name': 'test', 'piece length': 42, 'pieces': '', 'files': [] } get_info_from_handle(self.libtorrent_download_impl.handle ).metadata = lambda: lt.bencode(torrent_dict) get_info_from_handle(self.libtorrent_download_impl.handle ).files = lambda: [mocked_file] self.libtorrent_download_impl.checkpoint = mocked_checkpoint self.libtorrent_download_impl.on_metadata_received_alert(None)
def test_on_metadata_received_alert(self): """ Testing whether the right operations happen when we receive metadata """ test_deferred = Deferred() def mocked_checkpoint(): test_deferred.callback(None) mocked_file = MockObject() mocked_file.path = 'test' self.libtorrent_download_impl.handle.trackers = lambda: [] self.libtorrent_download_impl.handle.save_resume_data = lambda: None self.libtorrent_download_impl.handle.rename_file = lambda *_: None with open(os.path.join(TESTS_DATA_DIR, "bak_single.torrent"), mode='rb') as torrent_file: encoded_metainfo = torrent_file.read() decoded_metainfo = bdecode(encoded_metainfo) get_info_from_handle(self.libtorrent_download_impl.handle).metadata = lambda: bencode(decoded_metainfo['info']) get_info_from_handle(self.libtorrent_download_impl.handle).files = lambda: [mocked_file] self.libtorrent_download_impl.checkpoint = mocked_checkpoint self.libtorrent_download_impl.session = MockObject() self.libtorrent_download_impl.session.lm = MockObject() self.libtorrent_download_impl.session.lm.torrent_db = None self.libtorrent_download_impl.handle.save_path = lambda: None self.libtorrent_download_impl.handle.prioritize_files = lambda _: None self.libtorrent_download_impl.get_save_path = lambda: '' self.libtorrent_download_impl.get_share_mode = lambda: False self.libtorrent_download_impl.on_metadata_received_alert(None) return test_deferred
def got_metainfo(self, infohash, timeout=False): with self.metainfo_lock: infohash_bin = binascii.unhexlify(infohash) if infohash in self.metainfo_requests: request_dict = self.metainfo_requests.pop(infohash) handle = request_dict['handle'] callbacks = request_dict['callbacks'] timeout_callbacks = request_dict['timeout_callbacks'] notify = request_dict['notify'] self._logger.debug('got_metainfo %s %s %s', infohash, handle, timeout) assert handle if handle: if callbacks and not timeout: metainfo = {"info": lt.bdecode(get_info_from_handle(handle).metadata())} trackers = [tracker.url for tracker in get_info_from_handle(handle).trackers()] peers = [] leechers = 0 seeders = 0 for peer in handle.get_peer_info(): peers.append(peer.ip) if peer.progress == 1: seeders += 1 else: leechers += 1 if trackers: if len(trackers) > 1: metainfo["announce-list"] = [trackers] metainfo["announce"] = trackers[0] else: metainfo["nodes"] = [] if peers and notify: self.notifier.notify(NTFY_TORRENTS, NTFY_MAGNET_GOT_PEERS, infohash_bin, len(peers)) metainfo["initial peers"] = peers metainfo["leechers"] = leechers metainfo["seeders"] = seeders self._add_cached_metainfo(infohash, metainfo) for callback in callbacks: callback(deepcopy(metainfo)) # let's not print the hashes of the pieces debuginfo = deepcopy(metainfo) del debuginfo['info']['pieces'] self._logger.debug('got_metainfo result %s', debuginfo) elif timeout_callbacks and timeout: for callback in timeout_callbacks: callback(infohash_bin) if handle: self.get_session().remove_torrent(handle, 1) if notify: self.notifier.notify(NTFY_TORRENTS, NTFY_MAGNET_CLOSE, infohash_bin)
def got_metainfo(self, infohash, timeout=False): with self.metainfo_lock: infohash_bin = unhexlify(infohash) if infohash in self.metainfo_requests: request_dict = self.metainfo_requests.pop(infohash) handle = request_dict['handle'] callbacks = request_dict['callbacks'] timeout_callbacks = request_dict['timeout_callbacks'] notify = request_dict['notify'] self._logger.debug('got_metainfo %s %s %s', infohash, handle, timeout) assert handle if handle: if callbacks and not timeout: metainfo = {"info": lt.bdecode(get_info_from_handle(handle).metadata())} trackers = [tracker.url for tracker in get_info_from_handle(handle).trackers()] peers = [] leechers = 0 seeders = 0 for peer in handle.get_peer_info(): peers.append(peer.ip) if peer.progress == 1: seeders += 1 else: leechers += 1 if trackers: if len(trackers) > 1: metainfo["announce-list"] = [trackers] metainfo["announce"] = trackers[0] else: metainfo["nodes"] = [] if peers and notify: self.notifier.notify(NTFY_TORRENTS, NTFY_MAGNET_GOT_PEERS, infohash_bin, len(peers)) metainfo["initial peers"] = peers metainfo["leechers"] = leechers metainfo["seeders"] = seeders self._add_cached_metainfo(infohash, metainfo) for callback in callbacks: callback(deepcopy(metainfo)) # let's not print the hashes of the pieces debuginfo = deepcopy(metainfo) del debuginfo['info']['pieces'] self._logger.debug('got_metainfo result %s', debuginfo) elif timeout_callbacks and timeout: for callback in timeout_callbacks: callback(infohash_bin) if handle: self.ltsession_metainfo.remove_torrent(handle, 1) if notify: self.notifier.notify(NTFY_TORRENTS, NTFY_MAGNET_CLOSE, infohash_bin)
def __init__(self, f, d): self._logger = logging.getLogger(self.__class__.__name__) self._file = f self._download = d pieces = self._download.tdef.get_pieces() self.pieces = [pieces[x:x + 20]for x in xrange(0, len(pieces), 20)] self.piecesize = self._download.tdef.get_piece_length() self.startpiece = get_info_from_handle(self._download.handle).map_file( self._download.get_vod_fileindex(), 0, 0) self.endpiece = get_info_from_handle(self._download.handle).map_file( self._download.get_vod_fileindex(), self._download.get_vod_filesize(), 0)
def __init__(self, f, d): self._logger = logging.getLogger(self.__class__.__name__) self._file = f self._download = d pieces = self._download.tdef.get_pieces() self.pieces = [pieces[x:x + 20]for x in xrange(0, len(pieces), 20)] self.piecesize = self._download.tdef.get_piece_length() self.startpiece = get_info_from_handle(self._download.handle).map_file( self._download.get_vod_fileindex(), 0, 0) self.endpiece = get_info_from_handle(self._download.handle).map_file( self._download.get_vod_fileindex(), self._download.get_vod_filesize(), 0)
def on_metadata_received_alert(self, alert): torrent_info = get_info_from_handle(self.handle) if not torrent_info: return metadata = {'info': lt.bdecode(torrent_info.metadata())} trackers = [tracker['url'] for tracker in self.handle.trackers()] if trackers: if len(trackers) > 1: metadata["announce-list"] = [trackers] else: metadata["announce"] = trackers[0] try: self.tdef = TorrentDef.load_from_dict(metadata) except ValueError as ve: self._logger.exception(ve) return try: torrent_files = lt.torrent_info(metadata).files() except RuntimeError: self._logger.warning("Torrent contains no files!") torrent_files = [] self.orig_files = [torrent_file.path.decode('utf-8') for torrent_file in torrent_files] self.set_corrected_infoname() self.set_filepieceranges() self.set_selected_files() self.checkpoint()
def get_vod_filesize(self): fileindex = self.get_vod_fileindex() torrent_info = get_info_from_handle(self.handle) if fileindex >= 0 and torrent_info: file_entry = torrent_info.file_at(fileindex) return file_entry.size return 0
def set_byte_priority(self, byteranges, priority): pieces = [] torrent_info = get_info_from_handle(self.handle) if not torrent_info: self._logger.info("LibtorrentDownloadImpl: could not get info from download handle") for fileindex, bytes_begin, bytes_end in byteranges: if fileindex >= 0 and torrent_info: # Ensure the we remain within the file's boundaries file_entry = torrent_info.file_at(fileindex) bytes_begin = min( file_entry.size, bytes_begin) if bytes_begin >= 0 else file_entry.size + (bytes_begin + 1) bytes_end = min(file_entry.size, bytes_end) if bytes_end >= 0 else file_entry.size + (bytes_end + 1) startpiece = torrent_info.map_file(fileindex, bytes_begin, 0).piece endpiece = torrent_info.map_file(fileindex, bytes_end, 0).piece + 1 startpiece = max(startpiece, 0) endpiece = min(endpiece, torrent_info.num_pieces()) pieces += range(startpiece, endpiece) else: self._logger.info("LibtorrentDownloadImpl: could not set priority for incorrect fileindex") if pieces: pieces = list(set(pieces)) self.set_piece_priority(pieces, priority)
def render_GET(self, request): """ .. http:get:: /download/(string: infohash)/torrent A GET request to this endpoint returns the .torrent file associated with the specified download. **Example request**: .. sourcecode:: none curl -X GET http://localhost:8085/downloads/4344503b7e797ebf31582327a5baae35b11bda01/torrent **Example response**: The contents of the .torrent file. """ download = self.session.get_download(self.infohash) if not download: return DownloadSpecificEndpoint.return_404(request) if not download.handle or not download.handle.is_valid() or not download.handle.has_metadata(): return DownloadSpecificEndpoint.return_404(request) torrent_info = get_info_from_handle(download.handle) t = create_torrent(torrent_info) torrent = t.generate() bencoded_torrent = bencode(torrent) request.setHeader(b'content-type', 'application/x-bittorrent') request.setHeader(b'Content-Disposition', 'attachment; filename=%s.torrent' % hexlify(self.infohash)) return bencoded_torrent
def render_GET(self, request): """ .. http:get:: /download/(string: infohash)/torrent A GET request to this endpoint returns the .torrent file associated with the specified download. **Example request**: .. sourcecode:: none curl -X GET http://localhost:8085/downloads/4344503b7e797ebf31582327a5baae35b11bda01/torrent **Example response**: The contents of the .torrent file. """ download = self.session.get_download(self.infohash) if not download: return DownloadSpecificEndpoint.return_404(request) if not download.handle or not download.handle.is_valid( ) or not download.handle.has_metadata(): return DownloadSpecificEndpoint.return_404(request) torrent_info = get_info_from_handle(download.handle) t = create_torrent(torrent_info) torrent = t.generate() bencoded_torrent = bencode(torrent) request.setHeader(b'content-type', 'application/x-bittorrent') request.setHeader( b'Content-Disposition', 'attachment; filename=%s.torrent' % hexlify(self.infohash)) return bencoded_torrent
def test_get_info_from_handle(self): mock_handle = MockObject() def mock_get_torrent_file(): raise RuntimeError mock_handle.torrent_file = mock_get_torrent_file self.assertIsNone(get_info_from_handle(mock_handle))
def test_get_info_from_handle(self): mock_handle = MockObject() def mock_get_torrent_file(): raise RuntimeError mock_handle.torrent_file = mock_get_torrent_file self.assertIsNone(get_info_from_handle(mock_handle))
def set_selected_files(self, selected_files=None): if not isinstance(self.tdef, TorrentDefNoMetainfo): if selected_files is None: selected_files = self.get_selected_files() else: DownloadConfigInterface.set_selected_files(self, selected_files) is_multifile = len(self.orig_files) > 1 commonprefix = os.path.commonprefix(self.orig_files) if is_multifile else u'' swarmname = commonprefix.partition(os.path.sep)[0] unwanteddir = os.path.join(swarmname, u'.unwanted') unwanteddir_abs = os.path.join(self.get_save_path().decode('utf-8'), unwanteddir) filepriorities = [] torrent_storage = get_info_from_handle(self.handle).files() for index, orig_path in enumerate(self.orig_files): filename = orig_path[len(swarmname) + 1:] if swarmname else orig_path if filename in selected_files or not selected_files: filepriorities.append(1) new_path = orig_path else: filepriorities.append(0) new_path = os.path.join(unwanteddir, '%s%d' % (hexlify(self.tdef.get_infohash()), index)) # as from libtorrent 1.0, files returning file_storage (lazy-iterable) if hasattr(lt, 'file_storage') and isinstance(torrent_storage, lt.file_storage): cur_path = torrent_storage.at(index).path.decode('utf-8') else: cur_path = torrent_storage[index].path.decode('utf-8') if cur_path != new_path: if not os.path.exists(unwanteddir_abs) and unwanteddir in new_path: try: os.makedirs(unwanteddir_abs) if sys.platform == "win32": ctypes.windll.kernel32.SetFileAttributesW( unwanteddir_abs, 2) # 2 = FILE_ATTRIBUTE_HIDDEN except OSError: self._logger.error("LibtorrentDownloadImpl: could not create %s" % unwanteddir_abs) # Note: If the destination directory can't be accessed, libtorrent will not be able to store the files. # This will result in a DLSTATUS_STOPPED_ON_ERROR. # Path should be unicode if Libtorrent is using std::wstring (on Windows), # else we use str (on Linux). try: self.handle.rename_file(index, new_path) except TypeError: self.handle.rename_file(index, new_path.encode("utf-8")) # if in share mode, don't change priority of the file if not self.get_share_mode(): self.handle.prioritize_files(filepriorities) self.unwanteddir_abs = unwanteddir_abs
def set_selected_files(self, selected_files=None): if not isinstance(self.tdef, TorrentDefNoMetainfo): if selected_files is None: selected_files = self.get_selected_files() else: DownloadConfigInterface.set_selected_files(self, selected_files) is_multifile = len(self.orig_files) > 1 commonprefix = os.path.commonprefix(self.orig_files) if is_multifile else u'' swarmname = commonprefix.partition(os.path.sep)[0] unwanteddir = os.path.join(swarmname, u'.unwanted') unwanteddir_abs = os.path.join(self.get_save_path().decode('utf-8'), unwanteddir) filepriorities = [] torrent_storage = get_info_from_handle(self.handle).files() for index, orig_path in enumerate(self.orig_files): filename = orig_path[len(swarmname) + 1:] if swarmname else orig_path if filename in selected_files or not selected_files: filepriorities.append(1) new_path = orig_path else: filepriorities.append(0) new_path = os.path.join(unwanteddir, '%s%d' % (hexlify(self.tdef.get_infohash()), index)) # as from libtorrent 1.0, files returning file_storage (lazy-iterable) if hasattr(lt, 'file_storage') and isinstance(torrent_storage, lt.file_storage): cur_path = torrent_storage.at(index).path.decode('utf-8') else: cur_path = torrent_storage[index].path.decode('utf-8') if cur_path != new_path: if not os.path.exists(unwanteddir_abs) and unwanteddir in new_path: try: os.makedirs(unwanteddir_abs) if sys.platform == "win32": ctypes.windll.kernel32.SetFileAttributesW( unwanteddir_abs, 2) # 2 = FILE_ATTRIBUTE_HIDDEN except OSError: self._logger.error("LibtorrentDownloadImpl: could not create %s" % unwanteddir_abs) # Note: If the destination directory can't be accessed, libtorrent will not be able to store the files. # This will result in a DLSTATUS_STOPPED_ON_ERROR. # Path should be unicode if Libtorrent is using std::wstring (on Windows), # else we use str (on Linux). try: self.handle.rename_file(index, new_path) except TypeError: self.handle.rename_file(index, new_path.encode("utf-8")) # if in share mode, don't change priority of the file if not self.get_share_mode(): self.handle.prioritize_files(filepriorities) self.unwanteddir_abs = unwanteddir_abs
def test_metadata_received_invalid_torrent_with_value_error(self): """ Testing whether the right operations happen when we receive metadata but the torrent info is invalid and throws Value Error """ def mocked_checkpoint(): raise RuntimeError("This code should not be reached!") mocked_file = MockObject() mocked_file.path = 'test' # The line below should trigger Value Error self.libtorrent_download_impl.handle.trackers = lambda: [{'url': 'no-DHT'}] get_info_from_handle(self.libtorrent_download_impl.handle).metadata = lambda: lt.bencode({}) get_info_from_handle(self.libtorrent_download_impl.handle).files = lambda: [mocked_file] self.libtorrent_download_impl.checkpoint = mocked_checkpoint self.libtorrent_download_impl.on_metadata_received_alert(None)
def get_byte_progress(self, byteranges, consecutive=False): pieces = [] for fileindex, bytes_begin, bytes_end in byteranges: if fileindex >= 0: # Ensure the we remain within the file's boundaries file_entry = get_info_from_handle(self.handle).file_at(fileindex) bytes_begin = min( file_entry.size, bytes_begin) if bytes_begin >= 0 else file_entry.size + (bytes_begin + 1) bytes_end = min(file_entry.size, bytes_end) if bytes_end >= 0 else file_entry.size + (bytes_end + 1) startpiece = get_info_from_handle(self.handle).map_file(fileindex, bytes_begin, 0).piece endpiece = get_info_from_handle(self.handle).map_file(fileindex, bytes_end, 0).piece + 1 startpiece = max(startpiece, 0) endpiece = min(endpiece, get_info_from_handle(self.handle).num_pieces()) pieces += range(startpiece, endpiece) else: self._logger.info("LibtorrentDownloadImpl: could not get progress for incorrect fileindex") pieces = list(set(pieces)) return self.get_piece_progress(pieces, consecutive)
def get_byte_progress(self, byteranges, consecutive=False): pieces = [] for fileindex, bytes_begin, bytes_end in byteranges: if fileindex >= 0: # Ensure the we remain within the file's boundaries file_entry = get_info_from_handle(self.handle).file_at(fileindex) bytes_begin = min( file_entry.size, bytes_begin) if bytes_begin >= 0 else file_entry.size + (bytes_begin + 1) bytes_end = min(file_entry.size, bytes_end) if bytes_end >= 0 else file_entry.size + (bytes_end + 1) startpiece = get_info_from_handle(self.handle).map_file(fileindex, bytes_begin, 0).piece endpiece = get_info_from_handle(self.handle).map_file(fileindex, bytes_end, 0).piece + 1 startpiece = max(startpiece, 0) endpiece = min(endpiece, get_info_from_handle(self.handle).num_pieces()) pieces += range(startpiece, endpiece) else: self._logger.info("LibtorrentDownloadImpl: could not get progress for incorrect fileindex") pieces = list(set(pieces)) return self.get_piece_progress(pieces, consecutive)
def get_dest_files(self, exts=None): """ You can give a list of extensions to return. If None: return all dest_files @return list of (torrent,disk) filename tuples. """ dest_files = [] for index, file_entry in enumerate(get_info_from_handle(self.handle).files()): if self.handle.file_priority(index) > 0: filename = file_entry.path ext = os.path.splitext(filename)[1].lstrip('.') if exts is None or ext in exts: dest_files.append((filename, os.path.join(self.get_dest_dir(), filename.decode('utf-8')))) return dest_files
def get_dest_files(self, exts=None): """ You can give a list of extensions to return. If None: return all dest_files @return list of (torrent,disk) filename tuples. """ dest_files = [] for index, file_entry in enumerate(get_info_from_handle(self.handle).files()): if self.handle.file_priority(index) > 0: filename = file_entry.path ext = os.path.splitext(filename)[1].lstrip('.') if exts is None or ext in exts: dest_files.append((filename, os.path.join(self.get_dest_dir(), filename.decode('utf-8')))) return dest_files
def get_vod_filesize(self): fileindex = self.get_vod_fileindex() if fileindex >= 0: file_entry = get_info_from_handle(self.handle).file_at(fileindex) return file_entry.size return 0
def get_vod_filesize(self): fileindex = self.get_vod_fileindex() if fileindex >= 0: file_entry = get_info_from_handle(self.handle).file_at(fileindex) return file_entry.size return 0