import wx
import sys
from socket import inet_aton
#from operator import itemgetter
from threading import Event,Timer,currentThread
from time import time,sleep
#from cStringIO import StringIO
from traceback import print_stack,print_exc
from binascii import unhexlify
from BitTornado.download_bt1 import BT1Download
from BitTornado.clock import clock
from Utility.constants import *#IGNORE:W0611
fromUtility.helpersgetfreespace
from Swapper.Worldmap.peer import BTPeer
from Swapper.CacheDB.CacheDBHandler import FriendDBHandler
from safeguiupdate import DelayedEventHandler
from Swapper.Overlay.permid import show_permid
DEBUG = False
################################################################
#
# Class: ABCEngine
#
# This is the part of a torrent object created while it is
# active. It handles processing of statistics and information
# from the BitTornado core.
#
################################################################
class ABCEngine(DelayedEventHandler):
def __init__(self, torrent, myid):
DelayedEventHandler.__init__(self)
self.doneflag = Event()
self.torrent = torrent
self.queue = torrent.queue
self.utility = torrent.utility
self.downsize = { 'old' : torrent.files.downsize,
'new' : 0.0 }
self.upsize = { 'old' : torrent.files.upsize,
'new' : 0.0 }
self.spewwait = time()
self.reannouncelast = 0
self.lastexternalannounce = ''
self.timers = { 'lastupload': time(),
'lastdownload': time(),
'infrequent': None }
self.btstatus = self.utility.lang.get('waiting')
self.progress = torrent.files.progress
self.eta = None
self.rate = { "down" : 0.0,
"up" : 0.0 }
self.numpeers = 0
self.numseeds = 0
self.numconnections = 0
self.numcopies = None
self.peeravg = None
self.totalspeed = 0.0
self.numerrortracker=0
self.lasterrortracker=0
self.current_ratesetting = None
self.seedingtimelastcheck = None
# To compute mean over several last values of uprate/downrate
self.pastrate = { "down" : [0.0] * 20,
"up" : [0.0] * 20 }
self.meanrate = { "down" : 0.0,
"up" : 0.0 }
# Keep track of if we have any connections
self.hasConnections = False
# New stuff....
self.controller = self.utility.controller
self.waiting = True
self.checking = False
self.working = False
self.seed = False
self.closed = False
self.status_err = ['']
self.status_errtime = 0
self.status_done = 0.0
self.response = self.torrent.metainfo
hash = unhexlify(self.torrent.infohash)
self.rawserver = self.controller.handler.newRawServer(hash, self.doneflag)
# TODO: Workaround for multiport not reporting
# external_connection_made properly
self.workarounds = { 'hasexternal': False }
self.color = 'color_startup'
self.downloadHelpers = []
self.fileProgressPostponeCounter = 0
btconfig = self.utility.getBTParams()
# 2fastbt_
if self.torrent.caller == "helper":
btconfig['role'] = 'helper'
btconfig['coordinator_permid'] = self.torrent.caller_data['coordinator_permid']
msg = self.utility.lang.get('helping_friend') + self.torrent.caller_data['friendname']
self.errormsgCallback( msg, "status")
else:
btconfig['role'] = 'coordinator'
btconfig['helpers_file'] = ''
# _2fastbt
# # TODO: setting check_hashes doesn't seem to work quite right
# #
# # Only do reseed resume if the size of the files matches
# # the size that they are supposed to be
# # (Should prevent errors in at least some cases)
# if self.torrent.files.getSpaceNeeded(realsize = False) == 0:
# if self.torrent.files.skipcheck or \
# (self.torrent.status.completed and self.utility.config.Read('skipcheck', "boolean")):
# btconfig['check_hashes'] = 0
# self.torrent.files.skipcheck = False
self.dow = BT1Download(self.display,
self.finished,
self.error,
self.controller.exchandler,
self.doneflag,
btconfig,
self.response,
hash,
myid,
self.rawserver,
self.controller.listen_port)
####################################
# BEGIN NEW STUFF (FOR SINGLE PORT)
####################################
def start(self):
# Delete the cache information if doing a hashcheck
# (only necessary to delete the cache if fastresume is enabled)
if ((self.torrent.status.value == STATUS_HASHCHECK) and
self.utility.config.Read('fastresume', "boolean")):
self.dow.appdataobj.deleteTorrentData(self.dow.infohash)
if not self.dow.saveAs(self.saveAs):
self.shutdown()
return
self._hashcheckfunc = self.dow.initFiles()
if not self._hashcheckfunc:
self.shutdown()
return
self.controller.hashchecksched(self.torrent)
def hashcheck_start(self, donefunc):
if self.is_dead():
self.shutdown()
return
self.waiting = False
self.checking = True
# Start infrequent tasks
self.InfrequentTasks()
self._hashcheckfunc(donefunc)
def hashcheck_callback(self):
self.checking = False
if self.is_dead():
self.shutdown()
return
if not self.dow.startEngine(ratelimiter = None):
self.shutdown()
return
self.dow.startRerequester()
self.statsfunc = self.dow.startStats()
if self.torrent.status.value != STATUS_HASHCHECK:
self.torrent.status.updateStatus(STATUS_ACTIVE)
self.torrent.files.setFilePriorities()
self.torrent.connection.setMaxInitiate()
# Set the spew flag if the detail window is shown
if self.torrent.dialogs.details is not None:
self.dow.spewflag.set()
# TODO: Workaround for multiport not reporting
# external_connection_made properly
self.dow.spewflag.set()
self.rawserver.start_listening(self.dow.getPortHandler())
self.working = True
def shutdown(self):
if DEBUG:
print "engine: Starting shutdown"
# Remove from the active torrents list
try:
del self.utility.torrents["active"][self.torrent]
except:
pass
self.stop_download_help()
# Cancel timer
try:
if self.timers['infrequent'] is not None:
self.timers['infrequent'].cancel()
except:
pass
if self.closed:
return
self.doneflag.set()
try:
if DEBUG:
print "engine: Shutting down SingleRawServer"
self.rawserver.shutdown()
except:
print_exc()
pass
try:
self.dow.shutdown()
except:
pass
self.waiting = False
self.checking = False
self.working = False
self.closed = True
self.controller.was_stopped(self.torrent)
def display(self, activity = None, fractionDone = None):
# really only used by StorageWrapper now
self.setActivity(activity)
if fractionDone is not None:
self.status_done = float(fractionDone)
def error(self, msg):
if DEBUG:
print "engine: Error",msg
if self.doneflag.isSet():
# delegate updating GUI error message to launchmany core, as
# we're dying.
self.controller.dying_engines_errormsg(self.torrent,msg,"error")
self.shutdown()
else:
self.errormsg(msg)
self.status_err.append(msg)
self.status_errtime = clock()
def saveAs(self, name, length, saveas, isdir):
return self.torrent.files.dest
def done(self, event = None):
self.torrent.set()
def is_dead(self):
return self.doneflag.isSet()
####################################
# END NEW STUFF (FOR SINGLE PORT)
####################################
def setActivity(self, activity):
if activity is not None:
activities = { "checking existing data": self.utility.lang.get('checkingdata'),
"allocating disk space": self.utility.lang.get('allocatingspace'),
"moving data": self.utility.lang.get('movingdata') }
self.btstatus = activities.get(activity, activity)
def onUpdateStatus(self, fractionDone = None,
timeEst = None, downRate = None, upRate = None,
activity = None, statistics = None, spew = None,
**kws):
self.invokeLater(self.updateStatus, [fractionDone, timeEst, downRate, upRate, activity, statistics, spew])
def updateStatus(self, fractionDone = None, timeEst = None, downRate = None, upRate = None, activity = None, statistics = None, spew = None):
# Just in case a torrent was finished
# but now isn't
self.torrent.status.completed = self.seed
# Get scrape data every 20 minutes
#############################################
if self.utility.config.Read('scrape', "boolean"):
self.torrent.actions.scrape()
# Get Display Data
#############################################
if fractionDone is not None and not self.seed:
self.progress = (float(fractionDone) * 100)
self.setActivity(activity)
if timeEst is not None:
self.eta = timeEst
else:
self.eta = None
if self.torrent.status.value != STATUS_PAUSE:
if not self.seed and downRate is not None:
self.rate['down'] = float(downRate)
if self.rate['down'] != 0.0:
self.timers['lastdownload'] = time()
else:
self.rate['down'] = 0.0
if upRate is not None:
self.rate['up'] = float(upRate)
self.pastrate['up'].append(self.rate['up'])
self.pastrate['up'].pop(0)
if self.rate['up'] != 0.0:
self.timers['lastupload'] = time()
# Compute mean uprate
total = sum(self.pastrate['up'])
self.meanrate['up'] = total / 20
else:
self.rate['up'] = 0.0
if statistics is not None:
self.numpeers = statistics.numPeers
self.numcopies = statistics.numCopies
self.peeravg = statistics.percentDone
# Update download, upload, and progress
self.downsize['new'] = float(statistics.downTotal)
self.upsize['new'] = float(statistics.upTotal)
self.torrent.files.updateProgress()
self.totalspeed = float(statistics.torrentRate)
self.numconnections = statistics.numPeers
if not self.seed:
self.numseeds = statistics.numSeeds
self.numconnections += statistics.numSeeds
else:
self.numseeds = statistics.numOldSeeds
else:
self.peeravg = None
self.numcopies = None
if self.seed:
self.countSeedingTime()
if self.torrent.status.isDoneUploading():
self.TerminateUpload()
# Update color
self.updateColor(statistics, spew)
# Update text strings
self.torrent.updateColumns([COL_PROGRESS,
COL_BTSTATUS,
COL_ETA,
COL_DLSPEED,
COL_ULSPEED,
COL_MESSAGE])
if statistics is not None:
# Share Ratio, #Seed, #Peer, #Copies, #Peer Avg Progress,
# Download Size, Upload Size, Total Speed
self.torrent.updateColumns([COL_RATIO,
COL_SEEDS,
COL_PEERS,
COL_COPIES,
COL_PEERPROGRESS,
COL_DLSIZE,
COL_ULSIZE,
COL_TOTALSPEED,
COL_SEEDTIME,
COL_CONNECTIONS])
# Update progress in details window
if statistics is not None:
# Arno: slow down file progress update by a factor for multi-file
# torrents. Otherwise if the torrent has many files, the client will
# come to a halt.
#print "engine: #files in stats",len(statistics.filecomplete)
if self.fileProgressPostponeCounter == 0:
self.torrent.files.updateFileProgress(statistics)
if getattr(statistics,"filecomplete",None) is not None:
self.fileProgressPostponeCounter = int(len(statistics.filecomplete)/1000)
else:
self.fileProgressPostponeCounter = 0
else:
self.fileProgressPostponeCounter -= 1
if spew and len(spew) > 0:
self.updateCaches(spew)
# try:
self.updateDetailWindow(statistics, spew)
# except Exception, msg:
# # Just in case the window gets set to "None"
# # or is destroyed first
# print Exception, msg
#
if self.torrent.status.value == STATUS_HASHCHECK and self.working:
# Skip on ahead to the normal procedure if the torrent was active
# before doing the hashcheck
activevalues = [ STATUS_ACTIVE, STATUS_PAUSE, STATUS_SUPERSEED ]
oldstatus = self.torrent.actions.oldstatus
if not oldstatus in activevalues:
self.shutdown()
return
else:
self.torrent.status.updateStatus(STATUS_ACTIVE)
# TODO: Workaround for multiport not reporting
# external_connection_made properly
def getExternalConnectionsMade(self, spew):
if self.workarounds['hasexternal']:
return True
# Consider at least one remote connection evidence of
# an external connection being made
for x in range(len(spew)):
if spew[x]['direction'] == 'R':
self.workarounds['hasexternal'] = True
# We can clear the spewflag now...
if self.torrent.dialogs.details is None:
self.dow.spewflag.clear()
return True
return False
def updateColor(self, statistics = None, spew = None):
##################################################
# Set colour :
##################################################
color = None
if currentThread().getName() != "MainThread":
print "engine: updateColor thread",currentThread()
print "engine: NOT MAIN THREAD"
print_stack()
if statistics is not None:
externalConnectionMade = statistics.external_connection_made
# TODO: Workaround for multiport not reporting
# external_connection_made properly
if externalConnectionMade:
self.workarounds['hasexternal'] = True
else:
externalConnectionMade = self.workarounds['hasexternal']
if not externalConnectionMade and spew is not None:
externalConnectionMade = self.getExternalConnectionsMade(spew)
if statistics is None:
color = 'color_startup' #Start up
self.hasConnections = False
elif statistics.numPeers + statistics.numSeeds + statistics.numOldSeeds == 0:
if statistics.last_failed:
#Disconnected
color = 'color_disconnected'
else:
#No connections
color = 'color_noconnections'
self.hasConnections = False
elif (not externalConnectionMade):
#No incoming
color = 'color_noincoming'
self.hasConnections = True
elif ((statistics.numSeeds + statistics.numOldSeeds == 0)
and ((self.seed and statistics.numCopies < 1)
or (not self.seed and statistics.numCopies2 < 1))):
#No completes
color = 'color_nocomplete'
self.hasConnections = True
else:
#All Good
color = 'color_good'
self.hasConnections = True
self.color = color
self.torrent.updateColor()
#
# Things that don't need to be done on every pass through updateStatus
#
def InfrequentTasks(self):
try:
if self.timers['infrequent'] is not None:
self.timers['infrequent'].cancel()
except:
pass
self.CheckTimeouts()
self.CheckDiskSpace()
# Should check diskspace more frequently
# while in the "allocating" stage
if self.waiting or self.checking:
nextcheck = 2
else:
nextcheck = 30
self.timers['infrequent'] = Timer(nextcheck, self.InfrequentTasks)
self.timers['infrequent'].start()
#
# Check to make sure that there's free diskspace
#
def CheckDiskSpace(self):
threshold = self.utility.config.Read('diskfullthreshold', "int")
# Disk checking is disabled
if threshold == 0:
return
# See how much more space the torrent needs
spaceneeded = self.torrent.files.getSpaceNeeded()
# Don't need to worry if the torrent already has
# as much space as it needs
if spaceneeded == 0L:
return
dest = self.torrent.files.getProcDest(pathonly = True)
if dest is None:
# Don't bother checking for space until the
# destination path exists
return
spaceleft = getfreespace(dest)
if spaceleft < long((2**20) * threshold):
message = self.utility.lang.get('diskfull') + \
" (" + self.utility.size_format(spaceleft) + ")"
self.errormsg(message)
self.actionhandlerCallback( STATUS_STOP, self.torrent )
#
# See if there's been a timeout
#
def CheckTimeouts(self):
# Check to see if we need to check for timeouts
if not self.torrent.connection.timeout or self.torrent.status.value == STATUS_PAUSE:
return
# Check no download transfer in 30 mins
# (when this torrent is leeching torrent)
##########################################
if not self.seed:
timeoutdownload = self.utility.config.Read('timeoutdownload')
if (timeoutdownload != 'oo'
and (time() - self.timers['lastdownload']) > (float(timeoutdownload)*60)):
self.ReducePrioandForceQueue()
return
# Check no upload transfer in 1 hour
# (when this torrent is seeding torrent)
##########################################
else:
timeoutupload = self.utility.config.Read('timeoutupload')
if ((timeoutupload != 'oo')
and (time() - self.timers['lastupload']) > (float(timeoutupload)*3600)):
self.ReducePrioandForceQueue()
return
def updateDetailWindow(self, statistics = None, spew = None):
#####################################################
# Detail Window display part
#####################################################
detailwin = self.torrent.dialogs.details
if detailwin is None or not detailwin.update:
return
detailpanel = detailwin.detailPanel
if statistics is not None:
detailpanel.updateFromABCTorrent()
if spew is not None and (time() - self.spewwait > 1):
self.updateSpewList(statistics, spew)
if statistics is not None:
detailpanel.storagestats1.SetLabel(" " + self.utility.lang.get('detailline1')
% (statistics.storage_active,
statistics.storage_new,
statistics.storage_dirty))
detailpanel.storagestats2.SetLabel(" "+ self.utility.lang.get('detailline2')
% (statistics.storage_numcomplete,
statistics.storage_totalpieces,
statistics.storage_justdownloaded,
statistics.storage_numflunked))
def updatePeersOnEarth(self, spew):
""" update peer_swarm, delete non-actived peers, insert newcoming peers
and update the position of peers in the earth panel
note: each torrent has an abcengine
"""
# set all peers as not alive and then only active all peers in spew
for ip in self.torrent.peer_swarm.keys():
self.torrent.peer_swarm[ip].active = False
for peer_info in spew:
ip = peer_info['ip']
if self.torrent.peer_swarm.has_key(ip): # if the peer exists, active it
self.torrent.peer_swarm[ip].updateBTInfo(peer_info)
self.torrent.peer_swarm[ip].active = True
else: # otherwise create a new peer, add it into peer_swarm and display it
new_peer = BTPeer(peer_info, self.torrent, self.rawserver)
new_peer.updateBTInfo(peer_info)
self.torrent.peer_swarm[ip] = new_peer
def updatePeerCache(self, spew):
pass
def updateFileCache(self, spew):
pass
def updateCaches(self, spew):
""" update peers on earth map, peer cache and file cache """
#print "update caches", len(spew), self
self.updatePeersOnEarth(spew)
self.updatePeerCache(spew) # the real update is in DownloaderFeedback.update*()
self.updateFileCache(spew)
def sortSpew(self, spew, sortcol, reversed):
tmplist = [(self.getSpewColumnValue(sortcol, line, spew), spew[line]) for \
line in range(len(spew))]
tmplist.sort()
if reversed:
tmplist.reverse()
return [x for (_, x) in tmplist]
def updateSpewList(self, statistics = None, spew = None):
detailwin = self.torrent.dialogs.details
if spew is None or detailwin is None or not detailwin.update:
return
self.spewwait = time()
spewList = detailwin.detailPanel.spewList
columns = spewList.columns
numcols = len(columns.active)
# (no point in doing anything if there aren't any columns to update)
if numcols == 0:
return
spewlen = len(spew) + 3
if statistics is not None:
kickbanlen = len(statistics.peers_kicked)+len(statistics.peers_banned)
if kickbanlen:
spewlen += kickbanlen + 1
if statistics.peers_kicked:
spewlen += 1
if statistics.peers_banned:
spewlen += 1
else:
kickbanlen = 0
try:
for x in range(spewlen-spewList.GetItemCount()):
i = wx.ListItem()
spewList.InsertItem(i)
for x in range(spewlen, spewList.GetItemCount()):
spewList.DeleteItem(len(spew) + 1)
except wx.PyDeadObjectError:
pass
tot_uprate = 0.0
tot_downrate = 0.0
sortcol, reversed = spewList.getSortInfo()
if sortcol >= 0:
spew = self.sortSpew(spew, sortcol, reversed)
# Disabling itemgetter sorts to remain 2.3 compatible
# Sort by uprate first
#spew.sort(None, key=itemgetter('uprate'))
#spew.reverse()
#if not self.torrent.status.completed:
# Then sort by downrate if not complete
#spew.sort(None, key=itemgetter('downrate'))
#spew.reverse()
for x in range(len(spew)):
for colid, rank in columns.active:
self.updateSpewColumnText(x, rank, self.getSpewColumnText(colid, x, spew))
tot_uprate += spew[x]['uprate']
tot_downrate += spew[x]['downrate']
x = len(spew)
for i in range(numcols):
self.updateSpewColumnText(x, i, '')
x += 1
for colid, rank in columns.active:
if colid == SPEW_IP:
text = self.utility.lang.get('TOTALS')
else:
text = ''
self.updateSpewColumnText(x, rank, text)
x += 1
for colid, rank in columns.active:
if colid == SPEW_UP:
text = self.utility.speed_format(tot_uprate, truncate = 0)
elif colid == SPEW_DOWN:
text = self.utility.speed_format(tot_downrate, truncate = 0)
elif colid == SPEW_DLSIZE and statistics is not None:
text = self.utility.size_format(float(statistics.downTotal))
elif colid == SPEW_ULSIZE and statistics is not None:
text = self.utility.size_format(float(statistics.upTotal))
else:
text = ''
self.updateSpewColumnText(x, rank, text)
if kickbanlen:
x += 1
for i in range(numcols):
self.updateSpewColumnText(x, i, '')
if statistics.peers_kicked:
x += 1
for colid, rank in columns.active:
if colid == SPEW_IP:
text = self.utility.lang.get('KICKED')
else:
text = ''
self.updateSpewColumnText(x, rank, text)
for ip in statistics.peers_kicked:
x += 1
for colid, rank in columns.active:
if colid == SPEW_IP:
text = ip[1]
else:
text = ''
self.updateSpewColumnText(x, rank, text)
if statistics.peers_banned:
x += 1
for colid, rank in columns.active:
if colid == SPEW_IP:
text = self.utility.lang.get('BANNED')
else:
text = ''
self.updateSpewColumnText(x, rank, text)
for ip in statistics.peers_banned:
x += 1
for colid, rank in columns.active:
if colid == SPEW_IP:
text = ip[1]
else:
text = ''
self.updateSpewColumnText(x, rank, text)
def updateSpewColumnText(self, line, colid, text):
detailwin = self.torrent.dialogs.details
if detailwin is None or not detailwin.update:
return
try:
detailwin.detailPanel.spewList.SetStringItem(line, colid, text)
except wx.PyDeadObjectError:
pass
def getSpewColumnValue(self, colid, line, spew):
if colid == SPEW_UNCHOKE:
return spew[line]
elif colid == SPEW_IP:
try:
ip = spew[line]['ip']
return inet_aton(ip)
except:
return ''
x = s.find('.')
if x == -1:
return 0
iptext = s[:x] + '.' + ''.join(s[x:].split('.'))
return eval(iptext)
elif colid == SPEW_LR:
return spew[line]['direction']
elif colid == SPEW_UP:
return spew[line]['uprate']
elif colid == SPEW_INTERESTED:
return spew[line]['uinterested']
elif colid == SPEW_CHOKING:
return spew[line]['uchoked']
elif colid == SPEW_DOWN:
return spew[line]['downrate']
elif colid == SPEW_INTERESTING:
return spew[line]['dinterested']
elif colid == SPEW_CHOKED:
return spew[line]['dchoked']
elif colid == SPEW_SNUBBED:
return spew[line]['snubbed']
elif colid == SPEW_DLSIZE:
return spew[line]['dtotal']
elif colid == SPEW_ULSIZE:
if spew[line]['utotal'] is not None:
return spew[line]['utotal']
else:
return 0
elif colid == SPEW_PEERPROGRESS:
return spew[line]['completed']
elif colid == SPEW_PEERSPEED:
if spew[line]['speed'] is not None:
return spew[line]['speed']
else:
return 0
elif colid == SPEW_PERMID:
if spew[line]['unauth_permid'] is not None:
text = '*Swapper*'
friends = FriendDBHandler().getFriends()
for friend in friends:
#print "SPEW COMPARING friend",show_permid(friend['permid']),"to spew",show_permid(spew[line]['unauth_permid'])
if friend['permid'] == spew[line]['unauth_permid']:
text = friend['name']
break
return text
else:
return ""
return 0
def getSpewColumnText(self, colid, line, spew):
text = None
starflag = { True : '*', False : ' ' }
if colid == SPEW_UNCHOKE:
text = starflag[spew[line]['optimistic']]
elif colid == SPEW_IP:
text = spew[line]['ip']
elif colid == SPEW_LR:
if spew[line]['direction'] == 'R':
text = self.utility.lang.get('spew_direction_remote')
else:
text = self.utility.lang.get('spew_direction_local')
elif colid == SPEW_UP:
if spew[line]['uprate'] > 100:
text = self.utility.speed_format(spew[line]['uprate'], truncate = 0, stopearly = "KB")
elif colid == SPEW_INTERESTED:
text = starflag[spew[line]['uinterested']]
elif colid == SPEW_CHOKING:
text = starflag[spew[line]['uchoked']]
elif colid == SPEW_DOWN:
if spew[line]['downrate'] > 100:
text = self.utility.speed_format(spew[line]['downrate'], truncate = 0, stopearly = "KB")
elif colid == SPEW_INTERESTING:
text = starflag[spew[line]['dinterested']]
elif colid == SPEW_CHOKED:
text = starflag[spew[line]['dchoked']]
elif colid == SPEW_SNUBBED:
text = starflag[spew[line]['snubbed']]
elif colid == SPEW_DLSIZE:
text = self.utility.size_format(float(spew[line]['dtotal']))
elif colid == SPEW_ULSIZE:
if spew[line]['utotal'] is not None:
text = self.utility.size_format(float(spew[line]['utotal']))
elif colid == SPEW_PEERPROGRESS:
text = '%.1f%%' % (float(int(spew[line]['completed']*1000))/10)
elif colid == SPEW_PEERSPEED:
if spew[line]['speed'] is not None:
text = self.utility.speed_format(spew[line]['speed'], truncate = 0)
elif colid == SPEW_PERMID:
if spew[line]['unauth_permid'] is not None:
text = '*Swapper*'
friends = FriendDBHandler().getFriends()
for friend in friends:
#print "SPEW COMPARING friend",show_permid(friend['permid']),"to spew",show_permid(spew[line]['unauth_permid'])
if friend['permid'] == spew[line]['unauth_permid']:
text = friend['name']
break
if text is None:
text = ""
return text
def errormsg(self, errormsg):
errors = {"problem connecting to tracker": self.utility.lang.get('trackererror_problemconnecting'),
"rejected by tracker": self.utility.lang.get('trackererror_rejected'),
"bad data from tracker": self.utility.lang.get('trackererror_baddata') }
try:
trackererror = False
for error in errors:
index = errormsg.lower().find(error)
if index != -1:
oldlen = len(error)
errormsg = errormsg[:index] + errors[error] + errormsg[index + oldlen:]
trackererror = True
break
if trackererror:
currenttime = time()
if self.lasterrortracker == 0:
self.lasterrortracker = currenttime
if (currenttime - self.lasterrortracker) < 120: #error with in 2 mins
self.numerrortracker += 1
else:
self.numerrortracker = 0
self.lasterrortracker = currenttime
except:
pass
self.errormsgCallback(errormsg, "error")
# If failed connecting tracker in parameter 'timeouttracker' mins
# reduce its priority and force to queue
################################################################
if self.torrent.connection.timeout and self.utility.config.Read('timeouttracker') != "oo":
try:
if self.numerrortracker > self.utility.config.Read('timeouttracker', "int"):
self.ReducePrioandForceQueue()
except:
pass
def errormsgCallback(self,msg,label):
self.invokeLater(self.onErrorMsg,[msg,label])
def onErrorMsg(self,msg,label):
self.torrent.changeMessage(msg, label)
def ReducePrioandForceQueue(self):
currentprio = self.torrent.prio
if currentprio < 4: #prio is not lowest
self.changePriority(currentprio + 1) #lower 1 prio
self.queueMe()
def changePriority(self,prio):
self.invokeLater(self.torrent.changePriority,[prio])
def countSeedingTime(self):
now = time()
if self.seedingtimelastcheck is None:
lastcheck = now
else:
lastcheck = self.seedingtimelastcheck
timelapse = now - lastcheck
self.torrent.connection.seedingtime += timelapse
if self.torrent.connection.getSeedOption('uploadoption') == "1":
self.torrent.connection.seedingtimeleft = self.torrent.connection.getTargetSeedingTime() - self.torrent.connection.seedingtime
elif self.torrent.connection.getSeedOption('uploadoption') == "2":
if self.meanrate['up'] > 0:
if self.torrent.files.downsize == 0.0 :
down = self.torrent.files.floattotalsize
else:
down = self.torrent.files.downsize
up = self.torrent.files.upsize
ratio = float(self.torrent.connection.getSeedOption('uploadratio'))
required = ((ratio / 100.0) * down) - up
newseedingtimeleft = required / self.meanrate['up']
delta = max(newseedingtimeleft/10, 2)
if abs(self.torrent.connection.seedingtimeleft - newseedingtimeleft) > delta:
# If timer value deviates from theoretical value by more then 10%, reset it to theoretical value
self.torrent.connection.seedingtimeleft = newseedingtimeleft
else:
# Keep on timing
self.torrent.connection.seedingtimeleft -= timelapse
if self.torrent.connection.seedingtimeleft < 0.1:
self.torrent.connection.seedingtimeleft = 0.1
else:
# Set to 366 days (= infinite)
self.torrent.connection.seedingtimeleft = 999999999999999
self.seedingtimelastcheck = now
def TerminateUpload(self):
# Terminate process
####################################################
# change: 5:Progress 6:BT Status
# clear : 8:ETA 10:DLSpeed 11:ULspeed
# 14:#seed 15:#peer 16:#copie 17:peer avg
# 20:total speed
#####################################################
self.torrent.status.completed = True
self.progress = 100.0
self.torrent.connection.stopEngine()
self.queue.updateAndInvoke()
def finished(self):
# Let main thread perform GUI updates
self.invokeLater(self.finished_callback)
def finished_callback(self):
self.seed = True
# seeding process
####################################################
# change: 5:Progress 6:BT Status
# clear : 8:ETA 10:DLSpeed
#####################################################
self.torrent.status.completed = True
self.progress = 100.0
self.torrent.files.updateProgress()
self.stop_download_help()
if self.torrent.status.isDoneUploading():
self.TerminateUpload()
# Update cols 5, 6, 8, 10
self.torrent.updateColumns([COL_PROGRESS,
COL_BTSTATUS,
COL_ETA,
COL_DLSPEED])
self.torrent.updateColor()
self.queue.updateAndInvoke()
def failed(self):
if self.utility.config.Read('failbehavior') == '0':
# Stop
self.actionhandlerCallback( STATUS_STOP, self.torrent )
else:
# Queue
self.queueMe()
# Only queue if other things are waiting
# that would start up by queuing this torrent
def queueMe(self):
# See what the next torrent to start would be if we queued
# this torrent
inactivetorrents = self.utility.queue.getInactiveTorrents(1)
if not inactivetorrents:
return
nexttorrent = inactivetorrents[0]
# See if this torrent would be started if queued
queuethis = False
if (nexttorrent.prio < self.torrent.prio):
queuethis = True
elif (nexttorrent.prio == self.torrent.prio) and (nexttorrent.listindex < self.torrent.listindex):
queuethis = True
if queuethis:
self.btstatus = self.utility.lang.get('queue')
self.actionhandlerCallback( STATUS_QUEUE, self.torrent )
def actionhandlerCallback(self,action,torrent):
self.invokeLater(self.onActionhandler,[action,torrent])
def onActionhandler(self,action,torrent):
""" as calls to procQUEUE and procSTOP are usually by event handlers
executed by the mainthread, let the mainthread call them here as well
to prevent GUI update problems.
"""
if action == STATUS_QUEUE:
self.utility.actionhandler.procQUEUE([torrent])
else:
self.utility.actionhandler.procSTOP([torrent])
def getDownloadhelpCoordinator(self):
return self.dow.coordinator
def stop_download_help(self):
if self.dow.coordinator is not None:
self.dow.coordinator.stop_all_help()
|