SF.net SVN: fclient: [70] trunk/fclient/fclient_widgets/download_widget.py
Status: Pre-Alpha
Brought to you by:
jurner
|
From: <jU...@us...> - 2007-11-12 10:37:49
|
Revision: 70
http://fclient.svn.sourceforge.net/fclient/?rev=70&view=rev
Author: jUrner
Date: 2007-11-12 02:37:52 -0800 (Mon, 12 Nov 2007)
Log Message:
-----------
too many a changes.. reworked testDDA, PersistentGet is now handled and God knows whatelse
Modified Paths:
--------------
trunk/fclient/fclient_widgets/download_widget.py
Modified: trunk/fclient/fclient_widgets/download_widget.py
===================================================================
--- trunk/fclient/fclient_widgets/download_widget.py 2007-11-12 10:36:01 UTC (rev 69)
+++ trunk/fclient/fclient_widgets/download_widget.py 2007-11-12 10:37:52 UTC (rev 70)
@@ -112,11 +112,14 @@
self._requestStatus = self.StatusNone
self._requestInitTime = time.time()
self._uri = uri
+
+ self._ddaTested = False
def setRequestError(self, event, params):
"""If an error occures, sets info about the error
- @param event: event that triggered the error
+ @param event: event that triggered the error (EventFetchError, EventProtocolError,
+ EventTestDDAComplete)
@param params: params passed along with the event
"""
self._requestError = (event, params)
@@ -250,6 +253,13 @@
def requestUri(self):
"""Returns the request uri of the item"""
return self._uri
+
+
+ def setDDATested(self, flag):
+ self._ddaTested = flag
+
+ def ddaTested(self):
+ return self._ddaTested
#*****************************************************************************
#
@@ -291,9 +301,7 @@
#****************************************************************************
#
#****************************************************************************
-
-#TODO: no idea if the node removes requests when complete or on error or
-# if RemovePersistentRequest has to be send explicitely
+#TODO: when closing we have to empty queues by sending ClientGet requests
class DownloadWidget(QtGui.QTreeWidget):
def __init__(self,
@@ -315,7 +323,7 @@
self._connectionName = connectionName
self._cfg = cfg if cfg is not None else config.Config(self)
self._directory = directory if directory is not None else self._cfg.defaultDownloadDir
- self._downloads = {
+ self._requests = {
'Downloads': {}, # identifier --> item / items currently downloading
'DownloadQueue': [], # (parent-item, uri) / items scheduled for download
'DownloadDirectories': {}, # directory --> (parentItem, uris) / directories scheduled for testDDA
@@ -350,14 +358,19 @@
# schedule initial uris for download
if uris is not None:
- self._downloads['DownloadDirectories'][self._directory] = (self, uris)
+ for uri in uris:
+ self.download(uri)
+
+ #self._requests['DownloadDirectories'][self._directory] = (self, uris)
def showEvent(self, event):
if not self._isCreated:
self._isCreated = True
self._fcpClient = self._cfg.fcpClientManager.newClient(self._connectionName, self.handleFcpClientConnected)
-
+ self._fcpClient.setVerbosity(self._fcpClient.Verbosity.Debug)
+
+
#############################################################
##
## handlers for Qt events
@@ -384,6 +397,7 @@
(self._fcpClient.events.ClientRequestInfo, self.handleFcpClientRequestInfo),
(self._fcpClient.events.SimpleProgress, self.handleFcpClientSimpleProgress),
(self._fcpClient.events.DataFound, self.handleFcpClientDataFound),
+ (self._fcpClient.events.PersistentGet, self.handleFcpClientPersistentGet),
(self._fcpClient.events.GetFailed, self.handleFcpClientGetFailed),
(self._fcpClient.events.IdentifierCollision, self.handleFcpClientIdentifierCollision),
@@ -395,7 +409,7 @@
event += observer
# register directories
- for directory in self._downloads['DownloadDirectories']:
+ for directory in self._requests['DownloadDirectories']:
self._fcpClient.testDDA(directory, wantWriteDirectory=True)
@@ -408,62 +422,72 @@
"""
"""
identifier = params['Identifier']
- item = self._downloads['Downloads'].get(identifier, None)
+ item = self._requests['Downloads'].get(identifier, None)
if item is not None:
- del self._downloads['Downloads'][identifier]
+ del self._requests['Downloads'][identifier]
- progress = item.progressWidget(self)
- status = DownloadItem.StatusDownloadComplete
+ mimeType = params.get('Metadata.ContentType', self._strings.unknown)
+ dataLength = params.get('DataLength', '')
+ try:
+ dataLength = int(dataLength)
+ except ValueError:
+ dataLength = self._strings.unknown
+ else:
+ dataLength = numbers.format_num_bytes(dataLength)
+
+ self._adjustItemStatus(item, DownloadItem.StatusDownloadComplete)
+ item.setRequestMimeType(mimeType)
+ item.setRequestSize(dataLength)
- item.setRequestStatus(status, self._strings.itemStatus[status])
- progress.setColors(
- colorBar=self._userSettings['ColorProgressBarDownloadComplete'],
- colorBg=self._userSettings['ColorProgressBgDownloadComplete']
- )
- #TODO: how to provide extra information to the user?
+ #TODO: how to provide extra error information to the user?
def handleFcpClientGetFailed(self, event, params):
identifier = params['Identifier']
- item = self._downloads['Downloads'].get(identifier, None)
+ item = self._requests['Downloads'].get(identifier, None)
if item is not None:
- del self._downloads['Downloads'][identifier]
+ del self._requests['Downloads'][identifier]
code = params['Code']
# handle file name collision
+ # ...always wait for this error and adjust filename if necessary
if code == self._fcpClient.ProtocolError.DiskTargetExists:
if item.requestStatus() == DownloadItem.StatusDownloading:
filename = os.path.join(
self.downloadDirectoryFromItem(item),
item.increaseRequestNamePostfix()
)
- identifier = self.uniqueDownloadsIdentifier(self._fcpClient.IdentifierPrefix.ClientGetFile)
+ identifier = self._uniqueDownloadsIdentifier(self._fcpClient.IdentifierPrefix.ClientGetFile)
uri = item.requestUri()
self._fcpClient.clientGetFile(uri, filename, identifier=identifier)
- self._downloads['Downloads'][identifier] = item
+ self._requests['Downloads'][identifier] = item
return
+ # handle testDDA
+ # ...always wait for this error and reinsert download on TestDDAComplete
+ elif code == self._fcpClient.ProtocolError.DDADenied:
+ if not item.ddaTested():
+ directory = self.downloadDirectoryFromItem(item)
+ if directory in self._requests['DownloadDirectories']:
+ self._requests['DownloadDirectories'][directory].append(item)
+ else:
+ self._requests['DownloadDirectories'][directory] = [item, ]
+ #NOTE: have to take care to not send neted TestDDAs
+ self._fcpClient.testDDA(directory, wantWriteDirectory=True)
+
+ self._requests['Downloads'][item.requestIdentifier()] = item
+ return
+
# handle error
- progress = item.progressWidget(self)
- status = DownloadItem.StatusError
-
- item.setRequestStatus(status, self._strings.itemStatus[status])
+ self._adjustItemStatus(item, DownloadItem.StatusError)
item.setRequestError(event, params)
- # get rid of progress pending indicator if necessary
- if progress.maximum() == progress.minimum() == 0:
- progress.setRange(0, 1)
- progress.setValue(0)
- progress.setColors(
- colorBar=self._userSettings['ColorProgressBarError'],
- colorBg=self._userSettings['ColorProgressBgError']
- )
#raise self._fcpClient.FetchError(params)
#TODO: not yet handled
- def handleFcpClientIdentifierCollision(self, vent, params):
+ def handleFcpClientIdentifierCollision(self, event, params):
pass
@@ -472,22 +496,21 @@
# check if there are sownloads queued
n = 0
maxNewDls = self._userSettings['MaxNewDownloadsPerHop']
- while self._downloads['DownloadQueue'] and n < maxNewDls:
+ while self._requests['DownloadQueue'] and n < maxNewDls:
n += 1
- parent, uri = self._downloads['DownloadQueue'].pop(0)
+ parent, uri = self._requests['DownloadQueue'].pop(0)
+ if parent is None:
+ parent = self
item = DownloadItem(parent, '', uri)
fcpUri = self._fcpClient.FcpUri(uri)
fileName = fcpUri.fileName()
fileName = namespace.unquote_uri(fileName)
- status = DownloadItem.StatusPending
-
+
item.setRequestName(fileName)
- item.setRequestStatus(status, self._strings.itemStatus[status])
+ self._adjustItemStatus(item, DownloadItem.StatusPending)
- #... more here
-
-
+
# check how many downloads are currently running
#TODO: bit sloppy coding here. We know how max downloads to start,
@@ -508,6 +531,7 @@
items[status].append(item)
downloadsToBeStarted = self._userSettings['MaxSimultaniousDownloads'] - itemsBusy
+
#TODO: sort by priority
def sortf(item1, item2):
@@ -517,54 +541,79 @@
if downloadsToBeStarted > 0:
downloadsRequestInfoComplete = items[DownloadItem.StatusRequestInfoComplete]
downloadsRequestInfoComplete.sort(cmp=sortf)
- while downloadsRequestInfoComplete:
+ while downloadsRequestInfoComplete and downloadsToBeStarted > 0:
downloadsToBeStarted -= 1
- if downloadsToBeStarted < 0:
- break
-
+
item = downloadsRequestInfoComplete.pop(0)
- filename = os.path.join( self.downloadDirectoryFromItem(item), item.requestName())
- identifier = self.uniqueDownloadsIdentifier(self._fcpClient.IdentifierPrefix.ClientGetFile)
- progress = item.progressWidget(self)
- status = DownloadItem.StatusDownloading
+ filename = os.path.join(self.downloadDirectoryFromItem(item), item.requestName())
+ identifier = self._uniqueDownloadsIdentifier(self._fcpClient.IdentifierPrefix.ClientGetFile)
uri = item.requestUri()
- progress.setRange(0, 0)
- progress.setValue(0)
- progress.setColors(
- colorBar=self._userSettings['ColorProgressBarDownloading'],
- colorBg=self._userSettings['ColorProgressBgDownloading']
- )
- item.setRequestStatus(status, self._strings.itemStatus[status])
+ # tell node to remove completed RequestInfo
+ self._fcpClient.removePersistentRequest(item.requestIdentifier())
+
+ item.setRequestIdentifier(identifier)
+ self._adjustItemStatus(item, DownloadItem.StatusDownloading)
self._fcpClient.clientGetFile(uri, filename, identifier=identifier)
- self._downloads['Downloads'][identifier] = item
+
+ self._requests['Downloads'][identifier] = item
# start pending downloads
if downloadsToBeStarted > 0:
downloadsPending = items[DownloadItem.StatusPending]
downloadsPending.sort(cmp=sortf)
- while downloadsPending:
+ while downloadsPending and downloadsToBeStarted > 0:
downloadsToBeStarted -= 1
- if downloadsToBeStarted < 0:
- break
-
- identifier = self.uniqueDownloadsIdentifier(self._fcpClient.IdentifierPrefix.ClientRequestInfo)
+
+ identifier = self._uniqueDownloadsIdentifier(self._fcpClient.IdentifierPrefix.ClientRequestInfo)
item = downloadsPending.pop(0)
- progress = progressbarwrap.ProgressBarEx(self)
- status = DownloadItem.StatusRequestInfo
uri = item.requestUri()
- progress.setRange(0, 0)
- progress.setValue(0)
- progress.setColors(
- colorBar=self._userSettings['ColorProgressBarRequestInfo'],
- colorBg=self._userSettings['ColorProgressBgRequestInfo']
- )
- item.setProgressWidget(self, progress)
- item.setRequestStatus(status, self._strings.itemStatus[status])
- self._fcpClient.clientRequestInfo(uri, identifier=identifier)
- self._downloads['Downloads'][identifier] = item
+ # ClientToken will be set to item parent identifier
+ parent = item.parent()
+ if parent is None:
+ clientToken = None
+ else:
+ clientToken = parent.requestIdentifier()
+ item.setRequestIdentifier(identifier)
+ self._adjustItemStatus(item, DownloadItem.StatusRequestInfo)
+ self._fcpClient.clientRequestInfo(uri, identifier=identifier, clientToken=clientToken)
+
+ self._requests['Downloads'][identifier] = item
+
+
+ def handleFcpClientPersistentGet(self, event, params):
+ identifier = params['Identifier']
+ item = self._requests['Downloads'].get(identifier, None)
+ if item is None:
+
+ #self._fcpClient.removePersistentRequest(identifier)
+ #return
+
+ #TODO: no idea if the node passes PersistentGet messages in insertion order
+ # ..if not, we will have to cache items and wait for parent item to arrive
+ #
+ clientToken = params.get('ClientToken', None)
+ parent = self
+ if clientToken is not None:
+ parent = self._requests['Downloads'].get(clientToken, None)
+ uri = params['URI']
+ item = DownloadItem(parent, identifier, uri)
+ fcpUri = self._fcpClient.FcpUri(uri)
+ fileName = fcpUri.fileName()
+ fileName = namespace.unquote_uri(fileName)
+ item.setRequestName(fileName)
+
+ self._requests['Downloads'][identifier] = item
+
+ if self._fcpClient.isClientGetFile(identifier):
+ status = DownloadItem.StatusDownloading
+ elif self._fcpClient.isClientRequestInfo(identifier):
+ status = DownloadItem.StatusRequestInfo
+
+ self._adjustItemStatus(item, status)
+
def handleFcpClientProtocolError(self, event, params):
identifier = params.get('Identifier', None)
@@ -572,17 +621,20 @@
#TDO: handle error
pass
else:
- item = self._downloads['Downloads'].get(identifier, None)
+ item = self._requests['Downloads'].get(identifier, None)
if item is not None:
self.handleFcpClientGetFailed(event, params)
+
+ #NOTE: ProtocolError seems to jump in before a request is inserted in
+ # the nodes queue. So we will not get informed about the request on next reconnect.
def handleFcpClientRequestInfo(self, event, params):
identifier = params['Identifier']
- item = self._downloads['Downloads'].get(identifier, None)
+ item = self._requests['Downloads'].get(identifier, None)
if item is not None:
- del self._downloads['Downloads'][identifier]
+ del self._requests['Downloads'][identifier]
status = DownloadItem.StatusRequestInfoComplete
mimeType = params.get('Metadata.ContentType', self._strings.unknown)
@@ -608,7 +660,7 @@
def handleFcpClientSimpleProgress(self, event, params):
identifier = params['Identifier']
- item = self._downloads['Downloads'].get(identifier, None)
+ item = self._requests['Downloads'].get(identifier, None)
if item is not None:
progress = item.progressWidget(self)
required=int(params['Required'])
@@ -627,19 +679,23 @@
writeAllowed = params.get('WriteDirectoryAllowed')
# check if there are items to be donloaded for the directory
- downloads = self._downloads['DownloadDirectories'].get(directory, None)
+ downloads = self._requests['DownloadDirectories'].get(directory, None)
if downloads is not None:
- del self._downloads['DownloadDirectories'][directory] # ???
+ del self._requests['DownloadDirectories'][directory]
if downloads:
if writeAllowed == self._fcpClient.FcpTrue:
- parent, uris = downloads
- for uri in uris:
- self.download(uri, parent=parent)
-
+ for item in downloads:
+ item.setDDATested(True)
+ filename = os.path.join(self.downloadDirectoryFromItem(item), item.requestName())
+ self._fcpClient.clientGetFile(item.requestUri(), filename, identifier=item.requestIdentifier())
+
+ #TODO: not tested
else:
- pass
- #TODO: write access denied, error
+ for item in downloads:
+ self._adjustItemStatus(item, DownloadItem.StatusError)
+ item.setRequestError(event, params)
+
#######################################################
##
@@ -647,7 +703,7 @@
##
#######################################################
def download(self, uri, parent=None):
- self._downloads['DownloadQueue'].append( (parent, uri) )
+ self._requests['DownloadQueue'].append( (parent, uri) )
#TODO: adjust priority?
@@ -658,6 +714,8 @@
#TODO: retranslate item status
+ #TODO: is not correct for items that are not downloaded to self._downloads.
+ # self._downloads may change and / or items from global queue may jump in
def downloadDirectoryFromItem(self, item):
"""Returns the dirctory an item should be downloaded to
@param item: (QTreeWidgetItem)
@@ -674,17 +732,81 @@
return os.path.join(*out)
- def uniqueDownloadsIdentifier(self, identifierPrefix):
+ #######################################################
+ ##
+ ## private methods
+ ##
+ #######################################################
+ def _adjustItemStatus(self, item, status):
+ """Adjusts an item to a specifed status
+ @param item: (L{DownloadItem}) to adjust
+ @param status: L{DownloadItem}.Status* to adjust the item to
+ @return: item
+ """
+ if status == DownloadItem.StatusPending:
+ pass
+
+ elif status == DownloadItem.StatusRequestInfo:
+ progress = progressbarwrap.ProgressBarEx(self)
+ item.setProgressWidget(self, progress)
+ progress.setRange(0, 0)
+ progress.setValue(0)
+ progress.setColors(
+ colorBar=self._userSettings['ColorProgressBarRequestInfo'],
+ colorBg=self._userSettings['ColorProgressBgRequestInfo']
+ )
+
+ elif status == DownloadItem.StatusDownloading:
+ progress = item.progressWidget(self)
+ #NOTE: when we connect to the node PersistenGet may pass GetFile requests,
+ # ...so make shure progessbar is available
+ if progress is None:
+ progress = progressbarwrap.ProgressBarEx(self)
+ item.setProgressWidget(self, progress)
+ progress.setRange(0, 0)
+ progress.setValue(0)
+ progress.setColors(
+ colorBar=self._userSettings['ColorProgressBarDownloading'],
+ colorBg=self._userSettings['ColorProgressBgDownloading']
+ )
+
+ elif status == DownloadItem.StatusDownloadComplete:
+ progress = item.progressWidget(self)
+ progress.setRange(0, 1)
+ progress.setValue(1)
+ progress.setColors(
+ colorBar=self._userSettings['ColorProgressBarDownloadComplete'],
+ colorBg=self._userSettings['ColorProgressBgDownloadComplete']
+ )
+
+ elif status == DownloadItem.StatusError:
+ progress = item.progressWidget(self)
+ # get rid of pending progress indicator if necessary
+ if progress.maximum() == progress.minimum() == 0:
+ progress.setRange(0, 1)
+ progress.setValue(0)
+ progress.setColors(
+ colorBar=self._userSettings['ColorProgressBarError'],
+ colorBg=self._userSettings['ColorProgressBgError']
+ )
+
+ else:
+ raise ValueError('Status not handled')
+
+ item.setRequestStatus(status, self._strings.itemStatus[status])
+ return item
+
+
+ def _uniqueDownloadsIdentifier(self, identifierPrefix):
"""Creates a new identifier that is unique to the internal sownloads dict
@param identifierPrefix: FcpClient.IdentifierPrefix.*
@return: (str) identifier
"""
while True:
identifier = identifierPrefix + self._fcpClient.newIdentifier()
- if identifier not in self._downloads['Downloads']:
+ if identifier not in self._requests['Downloads']:
return identifier
-
#***************************************************************************************************
#
#***************************************************************************************************
@@ -708,8 +830,9 @@
w = QtGui.QMainWindow()
peers = DownloadWidget(None,
connectionName='TestDownloadWidget',
- uris=TestUris
-
+ #uris=None,
+ #uris=TestUris,
+ #uris=[TestUris[1], ]
)
w.setCentralWidget(peers)
This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site.
|