diff --git a/pisi/fetcher.py b/pisi/fetcher.py index 3d67e2df..80e09233 100644 --- a/pisi/fetcher.py +++ b/pisi/fetcher.py @@ -19,6 +19,7 @@ import urllib2 import os from base64 import encodestring +from urllib import addinfourl from shutil import move import gettext @@ -53,12 +54,14 @@ class Fetcher: if not isinstance(url, URI): url = URI(url) + self.scheme = url.scheme() self.url = url self.filedest = dest util.check_dir(self.filedest) self.percent = 0 self.rate = 0.0 self.progress = None + self.existsize = 0 def fetch (self): """Return value: Fetched file's full path..""" @@ -69,27 +72,27 @@ class Fetcher: if not os.access(self.filedest, os.W_OK): self.err(_("Access denied to write to dest dir")) - archiveFile = os.path.join(self.filedest, self.url.filename()) + archivefile = os.path.join(self.filedest, self.url.filename()) if self.url.is_local_file(): - self.fetchLocalFile(archiveFile + ".part") + self.fetchLocalFile(archivefile + ".part") else: - self.fetchRemoteFile(archiveFile + ".part") + self.fetchRemoteFile(archivefile + ".part") - move(archiveFile + ".part", archiveFile) - return archiveFile + move(archivefile + ".part", archivefile) + return archivefile def _do_grab(self, fileURI, dest, totalsize): symbols = [' B/s', 'KB/s', 'MB/s', 'GB/s'] from time import time tt, oldsize = int(time()), 0 - bs, size = 1024, 0 + bs, size = 1024, self.existsize symbol, depth = "B/s", 0 st = time() chunk = fileURI.read(bs) size = size + len(chunk) if self.progress: - p = self.progress(totalsize) + p = self.progress(totalsize, self.existsize) self.percent = p.update(size) while chunk: dest.write(chunk) @@ -114,40 +117,46 @@ class Fetcher: dest.close() - def fetchLocalFile (self, archiveFile): + def fetchLocalFile (self, archivefile): url = self.url if not os.access(url.path(), os.F_OK): self.err(_("No such file or no permission to read")) - dest = open(archiveFile, "w") + dest = open(archivefile, "w") totalsize = os.path.getsize(url.path()) fileObj = open(url.path()) self._do_grab(fileObj, dest, totalsize) - def fetchRemoteFile (self, archiveFile): + def fetchRemoteFile (self, archivefile): from httplib import HTTPException + if os.path.exists(archivefile) and \ + self.scheme == "http" or self.scheme == "https": + self.existsize = os.path.getsize(archivefile) + dest = open(archivefile, "ab") + else: + dest = open(archivefile, "wb") + uri = self.url.uri + try: fileObj = urllib2.urlopen(self.formatRequest(urllib2.Request(uri))) headers = fileObj.info() - except ValueError, e: - self.err('Cannot fetch %s; value error: %s' % (uri, e)) + self.err(_('Cannot fetch %s; value error: %s') % (uri, e)) except IOError, e: - self.err('Cannot fetch %s; %s' % (uri, e)) + self.err(_('Cannot fetch %s; %s') % (uri, e)) except OSError, e: - self.err('Cannot fetch %s; %s' % (uri, e)) + self.err(_('Cannot fetch %s; %s') % (uri, e)) except HTTPException, e: - self.err(('Cannot fetch %s; (%s): %s') % (uri, e.__class__.__name__, e)) + self.err(_('Cannot fetch %s; (%s): %s') % (uri, e.__class__.__name__, e)) try: - totalsize = int(headers['Content-Length']) + totalsize = int(headers['Content-Length']) + self.existsize except: totalsize = 0 - dest = open(archiveFile, "w") self._do_grab(fileObj, dest, totalsize) def formatRequest(self, request): @@ -155,8 +164,26 @@ class Fetcher: if authinfo: enc = encodestring("%s:%s" % authinfo) request.add_header('Authorization', 'Basic %s' % enc) + if self.existsize and self.scheme == "http" or self.scheme == "https": + range_handler = HTTPRangeHandler() + opener = urllib2.build_opener(range_handler) + urllib2.install_opener(opener) + request.add_header('Range', 'bytes=%d-' % self.existsize) return request def err (self, error): raise Error(error) +class HTTPRangeHandler(urllib2.BaseHandler): + """ + "to override the urllib2 error: 'Error 206: Partial Content' + this reponse from the HTTP server is already what we expected to get. + Don't give up, resume downloading.. + """ + + def http_error_206(self, request, fp, errcode, msg, headers): + return addinfourl(fp, headers, request.get_full_url()) + + def http_error_416(self, request, fp, errcode, msg, headers): + # HTTP 1.1's 'Range Not Satisfiable' error.. + raise FetchError(_('Requested range not satisfiable')) diff --git a/pisi/ui.py b/pisi/ui.py index dda674d7..60c842f2 100644 --- a/pisi/ui.py +++ b/pisi/ui.py @@ -12,7 +12,7 @@ # generic user interface # # Authors: Eray Ozkural -# Murat Eren +# A. Murat Eren import sys @@ -23,9 +23,9 @@ class UI(object): "Abstract class for UI operations, derive from this." class Progress: - def __init__(self, totalsize): + def __init__(self, totalsize, existsize = 0): self.totalsize = totalsize - self.percent = 0 + self.percent = (existsize * 100) / totalsize def update(self, size): if not self.totalsize: