📄 download.py
字号:
# The contents of this file are subject to the BitTorrent Open Source License# Version 1.0 (the License). You may not copy or use this file, in either# source code or executable form, except in compliance with the License. You# may obtain a copy of the License at http://www.bittorrent.com/license/.## Software distributed under the License is distributed on an AS IS basis,# WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License# for the specific language governing rights and limitations under the# License.# Written by Bram Cohen and Uoti Urpalafrom __future__ import division# required for python 2.2from __future__ import generatorsimport osimport sysimport threadingimport errnoimport gcfrom sha import shafrom socket import error as socketerrorfrom random import seedfrom time import timefrom cStringIO import StringIOfrom traceback import print_excfrom math import sqrttry: getpid = os.getpidexcept AttributeError: def getpid(): return 1from BitTorrent.btformats import check_messagefrom BitTorrent.Choker import Chokerfrom BitTorrent.Storage import Storage, FilePoolfrom BitTorrent.StorageWrapper import StorageWrapperfrom BitTorrent.Uploader import Uploadfrom BitTorrent.Downloader import Downloaderfrom BitTorrent.Encoder import Encoder, SingleportListenerfrom BitTorrent.RateLimiter import RateLimiterfrom BitTorrent.RawServer import RawServerfrom BitTorrent.Rerequester import Rerequesterfrom BitTorrent.DownloaderFeedback import DownloaderFeedbackfrom BitTorrent.RateMeasure import RateMeasurefrom BitTorrent.CurrentRateMeasure import Measurefrom BitTorrent.PiecePicker import PiecePickerfrom BitTorrent.ConvertedMetainfo import set_filesystem_encodingfrom BitTorrent import versionfrom BitTorrent import BTFailure, BTShutdown, INFO, WARNING, ERROR, CRITICALclass Feedback(object): def finished(self, torrent): pass def failed(self, torrent, is_external): pass def error(self, torrent, level, text): pass def exception(self, torrent, text): self.error(torrent, CRITICAL, text) def started(self, torrent): passclass Multitorrent(object): def __init__(self, config, doneflag, errorfunc, listen_fail_ok=False): self.config = dict(config) self.errorfunc = errorfunc self.rawserver = RawServer(doneflag, config, errorfunc=errorfunc, tos=config['peer_socket_tos']) self.singleport_listener = SingleportListener(self.rawserver) self._find_port(listen_fail_ok) self.filepool = FilePool(config['max_files_open']) self.ratelimiter = RateLimiter(self.rawserver.add_task) self.ratelimiter.set_parameters(config['max_upload_rate'], config['upload_unit_size']) set_filesystem_encoding(config['filesystem_encoding'], errorfunc) def _find_port(self, listen_fail_ok=True): e = 'maxport less than minport - no ports to check' if self.config['minport'] <= 0: self.config['minport'] = 1 for port in xrange(self.config['minport'], self.config['maxport'] + 1): try: self.singleport_listener.open_port(port, self.config) break except socketerror, e: pass else: if not listen_fail_ok: raise BTFailure, "Couldn't open a listening port: " + str(e) self.errorfunc(CRITICAL, "Could not open a listening port: " + str(e) + ". Check your port range settings.") def close_listening_socket(self): self.singleport_listener.close_sockets() def start_torrent(self, metainfo, config, feedback, filename): torrent = _SingleTorrent(self.rawserver, self.singleport_listener, self.ratelimiter, self.filepool, config) self.rawserver.add_context(torrent) def start(): torrent.start_download(metainfo, feedback, filename) self.rawserver.add_task(start, 0, torrent) return torrent def set_option(self, option, value): if option not in self.config or self.config[option] == value: return if option not in 'max_upload_rate upload_unit_size '\ 'max_files_open minport maxport'.split(): return self.config[option] = value if option == 'max_files_open': self.filepool.set_max_files_open(value) elif option == 'max_upload_rate': self.ratelimiter.set_parameters(value, self.config['upload_unit_size']) elif option == 'upload_unit_size': self.ratelimiter.set_parameters(self.config['max_upload_rate'], value) elif option == 'maxport': if not self.config['minport'] <= self.singleport_listener.port <= \ self.config['maxport']: self._find_port() def get_completion(self, config, metainfo, save_path, filelist=False): if not config['data_dir']: return None infohash = metainfo.infohash if metainfo.is_batch: myfiles = [os.path.join(save_path, f) for f in metainfo.files_fs] else: myfiles = [save_path] if metainfo.total_bytes == 0: if filelist: return None return 1 try: s = Storage(None, None, zip(myfiles, metainfo.sizes), check_only=True) except: return None filename = os.path.join(config['data_dir'], 'resume', infohash.encode('hex')) try: f = file(filename, 'rb') except: f = None try: r = s.check_fastresume(f, filelist, metainfo.piece_length, len(metainfo.hashes), myfiles) except: r = None if f is not None: f.close() if r is None: return None if filelist: return r[0] / metainfo.total_bytes, r[1], r[2] return r / metainfo.total_bytesclass _SingleTorrent(object): def __init__(self, rawserver, singleport_listener, ratelimiter, filepool, config): self._rawserver = rawserver self._singleport_listener = singleport_listener self._ratelimiter = ratelimiter self._filepool = filepool self.config = dict(config) self._storage = None self._storagewrapper = None self._ratemeasure = None self._upmeasure = None self._downmeasure = None self._encoder = None self._rerequest = None self._statuscollecter = None self._announced = False self._listening = False self.reserved_ports = [] self.reported_port = None self._myfiles = None self.started = False self.is_seed = False self.closed = False self.infohash = None self.total_bytes = None self._doneflag = threading.Event() self.finflag = threading.Event() self._hashcheck_thread = None self._contfunc = None self._activity = ('Initial startup', 0) self.feedback = None self.errors = [] def start_download(self, *args, **kwargs): it = self._start_download(*args, **kwargs) def cont(): try: it.next() except StopIteration: self._contfunc = None def contfunc(): self._rawserver.external_add_task(cont, 0, self) self._contfunc = contfunc contfunc() def _start_download(self, metainfo, feedback, save_path): self.feedback = feedback config = self.config self._set_auto_uploads() self.infohash = metainfo.infohash self.total_bytes = metainfo.total_bytes if not metainfo.reported_errors: metainfo.show_encoding_errors(self._error) myid = self._make_id() seed(myid) def schedfunc(func, delay): self._rawserver.add_task(func, delay, self) def externalsched(func, delay): self._rawserver.external_add_task(func, delay, self) if metainfo.is_batch: myfiles = [os.path.join(save_path, f) for f in metainfo.files_fs] else: myfiles = [save_path] self._filepool.add_files(myfiles, self) self._myfiles = myfiles self._storage = Storage(config, self._filepool, zip(myfiles, metainfo.sizes)) resumefile = None if config['data_dir']: filename = os.path.join(config['data_dir'], 'resume', self.infohash.encode('hex')) if os.path.exists(filename): try: resumefile = file(filename, 'rb') if self._storage.check_fastresume(resumefile) == 0: resumefile.close() resumefile = None except Exception, e: self._error(WARNING, 'Could not load fastresume data: '+ str(e) + '. Will perform full hash check.') if resumefile is not None: resumefile.close() resumefile = None def data_flunked(amount, index): self._ratemeasure.data_rejected(amount) self._error(INFO, 'piece %d failed hash check, ' 're-downloading it' % index) backthread_exception = [] def errorfunc(level, text): def e(): self._error(level, text) externalsched(e, 0) def hashcheck(): def statusfunc(activity = None, fractionDone = 0): if activity is None: activity = self._activity[0] self._activity = (activity, fractionDone) try: self._storagewrapper = StorageWrapper(self._storage, config, metainfo.hashes, metainfo.piece_length, self._finished, statusfunc, self._doneflag, data_flunked,
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -