diff --git a/deluge/i18n/POTFILES.in b/deluge/i18n/POTFILES.in index eff041dce..d2f40b698 100644 --- a/deluge/i18n/POTFILES.in +++ b/deluge/i18n/POTFILES.in @@ -1,5 +1,4 @@ deluge/error.py -deluge/maketorrent.py deluge/common.py deluge/rencode.py deluge/httpdownloader.py diff --git a/deluge/maketorrent.py b/deluge/maketorrent.py deleted file mode 100644 index 585dcaa4e..000000000 --- a/deluge/maketorrent.py +++ /dev/null @@ -1,356 +0,0 @@ -# -# maketorrent.py -# -# Copyright (C) 2009 Andrew Resch -# -# Deluge is free software. -# -# You may redistribute it and/or modify it under the terms of the -# GNU General Public License, as published by the Free Software -# Foundation; either version 3 of the License, or (at your option) -# any later version. -# -# deluge is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. -# See the GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with deluge. If not, write to: -# The Free Software Foundation, Inc., -# 51 Franklin Street, Fifth Floor -# Boston, MA 02110-1301, USA. -# -# In addition, as a special exception, the copyright holders give -# permission to link the code of portions of this program with the OpenSSL -# library. -# You must obey the GNU General Public License in all respects for all of -# the code used other than OpenSSL. If you modify file(s) with this -# exception, you may extend this exception to your version of the file(s), -# but you are not obligated to do so. If you do not wish to do so, delete -# this exception statement from your version. If you delete this exception -# statement from all source files in the program, then also delete it here. -# -# - -import sys -import os -from hashlib import sha1 as sha - -from deluge.common import get_path_size -from deluge.bencode import bencode, bdecode - -class InvalidPath(Exception): - """ - Raised when an invalid path is supplied - """ - pass - -class InvalidPieceSize(Exception): - """ - Raised when an invalid piece size is set. Piece sizes must be multiples of - 16KiB. - """ - pass - -class TorrentMetadata(object): - """ - This class is used to create .torrent files. - - ** Usage ** - - >>> t = TorrentMetadata() - >>> t.data_path = "/tmp/torrent" - >>> t.comment = "My Test Torrent" - >>> t.trackers = [["http://tracker.openbittorent.com"]] - >>> t.save("/tmp/test.torrent") - - """ - def __init__(self): - self.__data_path = None - self.__piece_size = 0 - self.__comment = "" - self.__private = False - self.__trackers = [] - self.__webseeds = [] - self.__pad_files = False - - def save(self, torrent_path, progress=None): - """ - Creates and saves the torrent file to `path`. - - :param torrent_path: where to save the torrent file - :type torrent_path: string - - :param progress: a function to be called when a piece is hashed - :type progress: function(num_completed, num_pieces) - - :raises InvalidPath: if the data_path has not been set - - """ - if not self.data_path: - raise InvalidPath("Need to set a data_path!") - - torrent = { - "info": {} - } - - if self.comment: - torrent["comment"] = self.comment.encode("UTF-8") - - if self.private: - torrent["info"]["private"] = True - - if self.trackers: - torrent["announce"] = self.trackers[0][0] - torrent["announce-list"] = self.trackers - else: - torrent["announce"] = "" - - if self.webseeds: - httpseeds = [] - webseeds = [] - for w in self.webseeds: - if w.endswith(".php"): - httpseeds.append(w) - else: - webseeds.append(w) - - if httpseeds: - torrent["httpseeds"] = httpseeds - if webseeds: - torrent["url-list"] = webseeds - - datasize = get_path_size(self.data_path) - - if self.piece_size: - piece_size = piece_size * 1024 - else: - # We need to calculate a piece size - piece_size = 16384 - while (datasize / piece_size) > 1024 and piece_size < (8192 * 1024): - piece_size *= 2 - - # Calculate the number of pieces we will require for the data - num_pieces = datasize / piece_size - if datasize % piece_size: - num_pieces += 1 - - torrent["info"]["piece length"] = piece_size - - # Create the info - if os.path.isdir(self.data_path): - torrent["info"]["name"] = os.path.split(self.data_path)[1] - files = [] - padding_count = 0 - # Collect a list of file paths and add padding files if necessary - for (dirpath, dirnames, filenames) in os.walk(self.data_path): - for index, filename in enumerate(filenames): - size = get_path_size(os.path.join(self.data_path, dirpath, filename)) - p = dirpath[len(self.data_path):] - p = p.lstrip("/") - p = p.split("/") - if p[0]: - p += [filename] - else: - p = [filename] - files.append((size, p)) - # Add a padding file if necessary - if self.pad_files and (index + 1) < len(filenames): - left = size % piece_size - if left: - p = list(p) - p[-1] = "_____padding_file_" + str(padding_count) - files.append((piece_size - left, p)) - padding_count += 1 - - # Run the progress function with 0 completed pieces - if progress: - progress(0, num_pieces) - - fs = [] - pieces = [] - # Create the piece hashes - buf = "" - for size, path in files: - path = [s.decode(sys.getfilesystemencoding()).encode("UTF-8") for s in path] - fs.append({"length": size, "path": path}) - if path[-1].startswith("_____padding_file_"): - buf += "\0" * size - pieces.append(sha(buf).digest()) - buf = "" - fs[-1]["attr"] = "p" - else: - fd = open(os.path.join(self.data_path, *path), "rb") - r = fd.read(piece_size - len(buf)) - while r: - buf += r - if len(buf) == piece_size: - pieces.append(sha(buf).digest()) - # Run the progress function if necessary - if progress: - progress(len(pieces), num_pieces) - buf = "" - else: - break - r = fd.read(piece_size - len(buf)) - fd.close() - - if buf: - pieces.append(sha(buf).digest()) - if progress: - progress(len(pieces), num_pieces) - buf = "" - - torrent["info"]["pieces"] = "".join(pieces) - torrent["info"]["files"] = fs - - elif os.path.isfile(self.data_path): - torrent["info"]["name"] = os.path.split(self.data_path)[1] - torrent["info"]["length"] = get_path_size(self.data_path) - pieces = [] - - fd = open(self.data_path, "rb") - r = fd.read(piece_size) - while r: - pieces.append(sha(r).digest()) - if progress: - progress(len(pieces), num_pieces) - - r = fd.read(piece_size) - - torrent["info"]["pieces"] = "".join(pieces) - - # Write out the torrent file - open(torrent_path, "wb").write(bencode(torrent)) - - def get_data_path(self): - """ - The path to the files that the torrent will contain. It can be either - a file or a folder. This property needs to be set before the torrent - file can be created and saved. - """ - return self.__data_path - - def set_data_path(self, path): - """ - :param path: the path to the data - :type path: string - - :raises InvalidPath: if the path is not found - - """ - if os.path.exists(path) and (os.path.isdir(path) or os.path.isfile(path)): - self.__data_path = os.path.abspath(path) - else: - raise InvalidPath("No such file or directory: %s" % path) - - def get_piece_size(self): - """ - The size of pieces in bytes. The size must be a multiple of 16KiB. - If you don't set a piece size, one will be automatically selected to - produce a torrent with less than 1024 pieces or the smallest possible - with a 8192KiB piece size. - - """ - return self.__piece_size - - def set_piece_size(self, size): - """ - :param size: the desired piece size in KiBs - :type size: int - - :raises InvalidPieceSize: if the piece size is not a multiple of 16 KiB - - """ - if size % 16 and size: - raise InvalidPieceSize("Piece size must be a multiple of 16 KiB") - self.__piece_size = size - - def get_comment(self): - """ - Comment is some extra info to be stored in the torrent. This is - typically an informational string. - """ - return self.__comment - - def set_comment(self, comment): - """ - :param comment: an informational string - :type comment: string - """ - self.__comment = comment - - def get_private(self): - """ - Private torrents only announce to the tracker and will not use DHT or - Peer Exchange. - - See: http://bittorrent.org/beps/bep_0027.html - - """ - return self.__private - - def set_private(self, private): - """ - :param private: True if the torrent is to be private - :type private: bool - """ - self.__private = private - - def get_trackers(self): - """ - The announce trackers is a list of lists. - - See: http://bittorrent.org/beps/bep_0012.html - - """ - return self.__trackers - - def set_trackers(self, trackers): - """ - :param trackers: a list of lists of trackers, each list is a tier - :type trackers: list of list of strings - """ - self.__trackers = trackers - - def get_webseeds(self): - """ - The web seeds can either be: - Hoffman-style: http://bittorrent.org/beps/bep_0017.html - or, - GetRight-style: http://bittorrent.org/beps/bep_0019.html - - If the url ends in '.php' then it will be considered Hoffman-style, if - not it will be considered GetRight-style. - """ - return self.__webseeds - - def set_webseeds(self, webseeds): - """ - :param webseeds: the webseeds which can be either Hoffman or GetRight style - :type webseeds: list of urls - """ - self.__webseeds = webseeds - - def get_pad_files(self): - """ - If this is True, padding files will be added to align files on piece - boundaries. - """ - return self.__pad_files - - def set_pad_files(self, pad): - """ - :param pad: set True to align files on piece boundaries - :type pad: bool - """ - self.__pad_files = pad - - data_path = property(get_data_path, set_data_path) - piece_size = property(get_piece_size, set_piece_size) - comment = property(get_comment, set_comment) - private = property(get_private, set_private) - trackers = property(get_trackers, set_trackers) - webseeds = property(get_webseeds, set_webseeds) - pad_files = property(get_pad_files, set_pad_files)