Example #1
0
class Torrent():
	def __init__(self, torrent_file, target_file, port = PEER_PORT,
		error_handler = None, tracker_retry_time = consts['TRACKER_RETRY_TIME']):
		self.running = False
		self.is_downloading = False
		self.completed = False
		self.paused = False

		self.peer_port = port
		self.error_handler = error_handler
		self.tracker_retry_time = tracker_retry_time

		self.data = read_torrent_file(torrent_file)

		self.info_hash = sha1(encode(self.data["info"])).digest()
		self.peer_id = generate_peer_id()
		self.handshake = generate_handshake(self.info_hash, self.peer_id)

		self.tracker_thread = None

		# check is seed
		self.target_file = target_file
		self.storage = Storage(self.data['info'])
		self.piece_num = self.storage.piece_num
		print 'piece_num:', self.piece_num
		import os
		if os.path.exists(target_file):
			self.storage.set_file(target_file)

		self.picker = PiecePicker(self)
		self.selector = PeerSelector(self)
		self.downloading = []
		self.count_received = {}

	def __del__(self):
		""" Stop the tracker thread. """
		if self.tracker_thread != None:
			self.tracker_loop.join()
		
	def handleError(self, err):
		print 'Error: ' + err + '\n\t' + traceback.format_exc()
		if self.error_handler != None:
			self.error_handler(err)

	def run(self):
		""" Start the torrent running. """

		try:
			if not self.running:
				self.running = True

				# run in main thread
				#self.tracker_loop = Thread(target = self.perform_tracker_request, \
				#	args = (self.data["announce"], self.info_hash, self.peer_id))
				#self.tracker_loop.start()
				self._perform_tracker_request(self.data["announce"], self.info_hash, self.peer_id)		
				self._perform_mainLoop()
				self._cleanup(self.data["announce"], self.info_hash, self.peer_id)

		except Exception as e:
			self.handleError(repr(e))

	def stop(self):
		""" Stop the torrent from running. """
		if self.running:
			self.running = False
			
			if self.is_downloading:
				self.is_downloading = False
				reactor.stop()

				self._cleanup(self.data["announce"], self.info_hash, self.peer_id)

			if self.tracker_thread != None:
				self.tracker_loop.join()

	def pause(self, paused = True):
		if self.paused == paused:
			return
		self.paused = paused
		if paused:
			# choke all
			for connection in self.torrent.connections.values():
				connection.choke()

	################################
	# Interface
	################################
	def newConnection(self, connection):
		self.connections[connection.peer_id] = connection
		print '[Torrent]\ttotal connected peers: ', len(self.connections)
		connection.bitfield(self.storage.gen_complete_str())
		

	def lostConnection(self, connection):
		if connection.peer_id in self.connections:
			del self.connections[connection.peer_id]
		self._cleanup_connection(connection)
		print '[Torrent]\ttotal connected peers: ', len(self.connections)
	################################
	# Event
	################################
	def onRequest(self, connection, block_info):
		if self.paused:
			return
		data = self.storage.get(*block_info)
		connection.piece(block_info, data)

	def onPiece(self, connection, block_info, block_data):
		if self.paused:
			return
		
		self.storage.push(block_info[0], block_info[1], block_data)
		if self.storage.is_piece_received(block_info[0]):
			self.pushHave(block_info[0])

			if self.storage.is_all_piece_received():
				# save target file
				self.storage.save_target_file(self.target_file)

				self.onComplete()

		request = (connection, block_info)
		self.downloading.remove(request)
		self._try_download()

		print 'push piece', block_info, len(block_data), ', rate:', repr(self.getDownloadRate() / 1000) + 'kb/s, complete:', repr(self.storage.get_downloaded_rate()*100)+'%'
		if connection.peer_id not in self.count_received.keys():
			self.count_received[connection.peer_id] = 1
		else:
			self.count_received[connection.peer_id] += 1

	def onCancel(self, connection, block_info):
		# now nothing to do
		pass

	def onUnchoked(self, connection):
		self._try_download()

	def onComplete(self):
		if self.completed:
			return

		time_used = time() - self.start_time
		if time_used == 0:
			time_used = 1e-7
		print '[Torrent]\tDownload Completed!', 'Total time:', time_used, 'Speed:', repr(self.storage.length / time_used / 1024) + 'kb/s'
		for peer_id in self.count_received:
			print '\t\tFrom #' + peer_id + ' received:', self.count_received[peer_id]
		self.completed = True

		# inform the tracker
		self.tracker_thread = Thread(target = self._inform_tracker_completed, \
			args = (self.data["announce"], self.info_hash, self.peer_id))
		self.tracker_thread.start()

	def pushHave(self, piece_index):
		for connection in self.connections.values():
			connection.have(piece_index)

	################################
	# Util
	################################
	def checkBlockInfo(self, block_info):
		if block_info[0] not in range(0, self.piece_num):
			return False
		return True	
	def getUsableConnections(self):
		ret = []
		in_use = [request[0].peer_id for request in self.downloading]
		for connection in self.connections.values():
			if (not connection.is_choked) and (connection.peer_id not in in_use):
				ret.append(connection)
		return ret
	def getUnchokedConnections(self):
		return [conn for conn in self.connections.values() if not conn.is_choked]

	def getDownloadRate(self):
		rate = 0
		for conn in self.connections.values():
			rate += conn.getDownloadRate()
		return rate
	def getUploadRate(self):
		rate = 0
		for conn in self.connections.values():
			rate += conn.getUploadRate()
		return rate

	def hasPiece(self, piece_index):
		return self.storage.is_piece_received(piece_index)
	def isSeed(self):
		return self.completed

	################################
	# Privates
	################################
	def _perform_mainLoop(self):
		""" Run torrent main logic """
		self.is_downloading = True
		self.connections = {}

		for peer_info in self.peers:
			reactor.connectTCP(peer_info[0], peer_info[1], BTPeerClientFactory(self))
		self._launch_timer()
		reactor.listenTCP(self.peer_port, BTPeerServerFactory(self))

		self.start_time = time()
		if self.storage.is_all_piece_received():
			self.onComplete()

		reactor.run()

	def _perform_tracker_request(self, url, info_hash, peer_id):
		""" Make a tracker request to url, every interval seconds, using
		the info_hash and peer_id, and decode the peers on a good response. """

		cnt = 0
		while self.running and cnt < self.tracker_retry_time:
			self.tracker_response = make_tracker_request(info_hash, peer_id, url, 
				peer_port = self.peer_port)

			if "failure reason" not in self.tracker_response:
				self.peers = get_peers(self.tracker_response["peers"])
				print '[Torrent]\tpeers:', self.peers
				return

			sleep(self.tracker_response["interval"])

		raise Exception('can not connect to tracker!')

	def _inform_tracker_completed(self, url, info_hash, peer_id):
		try:
			self.tracker_response = make_tracker_request(info_hash, peer_id, url, 
				event = 'completed', peer_port = self.peer_port)
			if "failure reason" not in self.tracker_response:
				print '[Torrent]\tinform tracker completed.'
		except Exception, e:
			pass
from storage import Storage

info = {}
info["piece_length"] = 524288
info["length"] = 524288 * 8
st = Storage(info)

print("is all piece received: ", st.is_all_piece_received())
for i in range(8):
	for j in range(32):
		st.push(i, j*16*1024, "sdfsdfsdf")
print("is all piece received: ", st.gen_priority_list())
print("is all piece received: ", st.is_all_piece_received())