def _create_file_info_dict(self, file_path, piece_size, include_md5_sum=True): """ Returns a dictionary with the following keys: - pieces: concatenated 20-byte SHA-1 hashes - name: basename of the file - length: size of the file in bytes - md5sum: md5sum of the file (if include_md5_sum is True) @rtype: dict """ if not os.path.isfile(file_path): msg = '"{path}" is not a file' raise TorrentError(msg.format(path=file_path)) if os.path.getsize(file_path) == 0: msg = '"{path}" is a zero byte file!' raise TorrentError(msg.format(path=file_path)) file_extension = os.path.splitext(file_path)[1].lower() if (self.extension_whitelist is not None) and (file_extension not in self.extension_whitelist): msg = '"{path}" is not a valid file type for upload to this tracker' raise TorrentError(msg.format(path=file_path)) # Concatenated 20-byte SHA-1 hashes of all the file's pieces pieces = bytearray() # Aggregate MD5 sum md5 = hashlib.md5() if include_md5_sum else None msg = 'Hashing file "{path}"... ' logging.info(msg.format(path=file_path)) file_pieces = create_piece_generator(file_path, piece_size) for piece in file_pieces: piece_hash = files.sha1(piece) pieces.extend(piece_hash) if include_md5_sum: md5.update(piece) info = { 'pieces': pieces, 'piece length': piece_size, 'name': os.path.basename(file_path), 'length': os.path.getsize(file_path), } if include_md5_sum: info['md5sum'] = md5.hexdigest() assert len(info['pieces']) % 20 == 0, 'len(pieces) is not a multiple of 20 bytes!' return info
def _create_directory_info_dict(self, root_dir_path, piece_size, include_md5_sum=True): """ Returns a dictionary with the following keys: - pieces: concatenated 20-byte SHA-1 hashes - name: basename of the directory (default name of all torrents) - files: a list of dictionaries with the following keys: - length: size of the file in bytes - md5sum: md5 sum of the file (unless disabled via include_md5) - path: list of the file's path components, relative to the directory @rtype: dict """ if not os.path.isdir(root_dir_path): msg = '"{path}" is not a directory' raise TorrentError(msg.format(path=root_dir_path)) if piece_size < (16 * KB): msg = 'Piece size {size} is less than 16 KiB!' raise TorrentError(msg.format(size=piece_size)) # Concatenated 20-byte SHA-1 hashes of all the torrent's pieces. info_pieces = bytearray() # This bytearray will be used for the calculation of info_pieces. # Consecutive files will be written into data_buffer as a continuous # stream, as required by the BitTorrent specification. data_buffer = bytearray() file_dicts = [] for (dir_path, dir_names, file_names) in os.walk(root_dir_path): for file_name in file_names: # If the file's extension isn't in the whitelist, ignore it file_extension = os.path.splitext(file_name)[1].lower() if (self.extension_whitelist is not None) and (file_extension not in self.extension_whitelist): continue file_path = os.path.join(dir_path, file_name) # Build the current file's dictionary. file_dict = { 'length': os.path.getsize(file_path), 'path': files.split_path(os.path.relpath(file_path, root_dir_path)) } # Keep track of the file's MD5 sum md5 = hashlib.md5() if include_md5_sum else None logging.info( 'Hashing file "{path}"... '.format( path=os.path.relpath(file_path, root_dir_path) ) ) file_pieces = create_piece_generator(file_path, piece_size) for piece in file_pieces: data_buffer.extend(piece) if len(data_buffer) >= piece_size: piece_hash = files.sha1(data_buffer[:piece_size]) info_pieces.extend(piece_hash) data_buffer[:] = data_buffer[piece_size:] if include_md5_sum: md5.update(piece) if include_md5_sum: file_dict['md5sum'] = md5.hexdigest() file_dicts.append(file_dict) # Hash any remaining data that is fewer than piece_size bytes if len(data_buffer) > 0: piece_hash = files.sha1(data_buffer) info_pieces.extend(piece_hash) info = { 'pieces': info_pieces, 'piece length': piece_size, 'name': os.path.basename(root_dir_path.strip(os.path.sep)), 'files': file_dicts, } assert len(info['pieces']) % 20 == 0, 'len(pieces) is not a multiple of 20 bytes!' return info