Пример #1
0
    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
Пример #2
0
    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