mirror support (python level)
This commit is contained in:
parent
1ac436a01f
commit
be34e2e587
|
|
@ -52,7 +52,7 @@ class DWBPackageInstaller(DirectWaitBar, PackageInstaller):
|
|||
downloadStarted() and downloadFinished() to indicate the start
|
||||
of a new package. """
|
||||
if self['updateText']:
|
||||
self['text'] = 'Installing %s' % (package.displayName)
|
||||
self['text'] = 'Installing %s' % (package.getFormattedName())
|
||||
self.show()
|
||||
|
||||
def packageProgress(self, package, progress):
|
||||
|
|
|
|||
|
|
@ -25,7 +25,8 @@ class HostInfo:
|
|||
# contents file is read.
|
||||
self.descriptiveName = None
|
||||
|
||||
# A list of known mirrors for this host.
|
||||
# A list of known mirrors for this host, all URL's guaranteed
|
||||
# to end with a slash.
|
||||
self.mirrors = []
|
||||
|
||||
# A map of keyword -> altHost URL's. An altHost is different
|
||||
|
|
@ -159,8 +160,11 @@ class HostInfo:
|
|||
xmirror = xhost.FirstChildElement('mirror')
|
||||
while xmirror:
|
||||
url = xmirror.Attribute('url')
|
||||
if url and url not in self.mirrors:
|
||||
self.mirrors.append(url)
|
||||
if url:
|
||||
if url[-1] != '/':
|
||||
url += '/'
|
||||
if url not in self.mirrors:
|
||||
self.mirrors.append(url)
|
||||
xmirror = xmirror.NextSiblingElement('mirror')
|
||||
|
||||
xalthost = xhost.FirstChildElement('alt_host')
|
||||
|
|
|
|||
|
|
@ -3,6 +3,7 @@ from direct.p3d.FileSpec import FileSpec
|
|||
from direct.showbase import VFSImporter
|
||||
import os
|
||||
import sys
|
||||
import random
|
||||
|
||||
class PackageInfo:
|
||||
|
||||
|
|
@ -52,12 +53,12 @@ class PackageInfo:
|
|||
|
||||
# These will be filled in by HostInfo when the package is read
|
||||
# from contents.xml.
|
||||
self.descFileUrl = None
|
||||
self.descFile = None
|
||||
self.importDescFile = None
|
||||
|
||||
# These are filled in when the desc file is successfully read.
|
||||
self.hasDescFile = False
|
||||
self.patchVersion = None
|
||||
self.displayName = None
|
||||
self.uncompressedArchive = None
|
||||
self.compressedArchive = None
|
||||
|
|
@ -90,14 +91,32 @@ class PackageInfo:
|
|||
|
||||
return size
|
||||
|
||||
def getFormattedName(self):
|
||||
""" Returns the name of this package, for output to the user.
|
||||
This will be the "public" name of the package, as formatted
|
||||
for user consumption; it will include capital letters and
|
||||
spaces where appropriate. """
|
||||
|
||||
if self.displayName:
|
||||
name = self.displayName
|
||||
else:
|
||||
name = self.packageName
|
||||
if self.packageVersion:
|
||||
name += ' %s' % (self.packageVersion)
|
||||
|
||||
if self.patchVersion:
|
||||
name += ' rev %s' % (self.patchVersion)
|
||||
|
||||
return name
|
||||
|
||||
|
||||
def setupFilenames(self):
|
||||
""" This is called by the HostInfo when the package is read
|
||||
from contents.xml, to set up the internal filenames and such
|
||||
that rely on some of the information from contents.xml. """
|
||||
|
||||
self.descFileUrl = self.host.hostUrlPrefix + self.descFile.filename
|
||||
|
||||
basename = self.descFile.filename.rsplit('/', 1)[-1]
|
||||
dirname, basename = self.descFile.filename.rsplit('/', 1)
|
||||
self.descFileDirname = dirname
|
||||
self.descFileBasename = basename
|
||||
|
||||
def checkStatus(self):
|
||||
|
|
@ -136,28 +155,21 @@ class PackageInfo:
|
|||
# We've already got one.
|
||||
return True
|
||||
|
||||
url = URLSpec(self.descFileUrl)
|
||||
print "Downloading desc file %s" % (url)
|
||||
|
||||
rf = Ramfile()
|
||||
channel = http.getDocument(url)
|
||||
if not channel.downloadToRam(rf):
|
||||
print "Unable to download %s" % (url)
|
||||
self.http = http
|
||||
if not self.__downloadFile(
|
||||
None, self.descFile,
|
||||
urlbase = self.descFile.filename,
|
||||
filename = self.descFileBasename):
|
||||
# Couldn't download the desc file.
|
||||
return False
|
||||
|
||||
filename = Filename(self.packageDir, self.descFileBasename)
|
||||
filename.makeDir()
|
||||
filename.unlink()
|
||||
f = open(filename.toOsSpecific(), 'wb')
|
||||
f.write(rf.getData())
|
||||
f.close()
|
||||
|
||||
# Now that we've written the desc file, make it read-only.
|
||||
os.chmod(filename.toOsSpecific(), 0444)
|
||||
|
||||
try:
|
||||
self.readDescFile()
|
||||
except ValueError:
|
||||
if not self.readDescFile():
|
||||
# Weird, it passed the hash check, but we still can't read
|
||||
# it.
|
||||
print "Failure reading %s" % (filename)
|
||||
return False
|
||||
|
||||
|
|
@ -165,24 +177,28 @@ class PackageInfo:
|
|||
|
||||
def readDescFile(self):
|
||||
""" Reads the desc xml file for this particular package.
|
||||
Presumably this has already been downloaded and installed. """
|
||||
Returns true on success, false on failure. """
|
||||
|
||||
if self.hasDescFile:
|
||||
# No need to read it again.
|
||||
return
|
||||
return True
|
||||
|
||||
filename = Filename(self.packageDir, self.descFileBasename)
|
||||
|
||||
doc = TiXmlDocument(filename.toOsSpecific())
|
||||
if not doc.LoadFile():
|
||||
raise ValueError
|
||||
return False
|
||||
|
||||
xpackage = doc.FirstChildElement('package')
|
||||
if not xpackage:
|
||||
raise ValueError
|
||||
return False
|
||||
|
||||
try:
|
||||
self.patchVersion = int(xpackage.Attribute('patch_version') or '')
|
||||
except ValueError:
|
||||
self.patchVersion = None
|
||||
|
||||
self.displayName = None
|
||||
|
||||
xconfig = xpackage.FirstChildElement('config')
|
||||
if xconfig:
|
||||
# The name for display to an English-speaking user.
|
||||
|
|
@ -216,9 +232,9 @@ class PackageInfo:
|
|||
if self.__checkArchiveStatus():
|
||||
# It's all good.
|
||||
self.hasPackage = True
|
||||
return
|
||||
return True
|
||||
|
||||
# We need to download an update.
|
||||
# Now set up to download the update.
|
||||
self.hasPackage = False
|
||||
|
||||
# Now determine what we will need to download, and build a
|
||||
|
|
@ -238,7 +254,7 @@ class PackageInfo:
|
|||
self.uncompressedArchive.actualFile = None
|
||||
if self.uncompressedArchive.quickVerify(self.packageDir):
|
||||
self.installPlans = [planA]
|
||||
return
|
||||
return True
|
||||
|
||||
# Maybe the compressed archive file is good.
|
||||
if self.compressedArchive.quickVerify(self.packageDir):
|
||||
|
|
@ -246,11 +262,11 @@ class PackageInfo:
|
|||
step = self.InstallStep(self.__uncompressArchive, uncompressSize, self.uncompressFactor)
|
||||
planA = [step] + planA
|
||||
self.installPlans = [planA]
|
||||
return
|
||||
return True
|
||||
|
||||
# Maybe we can download one or more patches. We'll come back
|
||||
# to that in a minute as plan A. For now, construct on plan
|
||||
# B, which will be to download the whole archive.
|
||||
# to that in a minute as plan A. For now, construct plan B,
|
||||
# which will be to download the whole archive.
|
||||
planB = planA[:]
|
||||
|
||||
uncompressSize = self.uncompressedArchive.size
|
||||
|
|
@ -285,6 +301,8 @@ class PackageInfo:
|
|||
# plan B as the only plan.
|
||||
self.installPlans = [planB]
|
||||
|
||||
return True
|
||||
|
||||
def __scanDirectoryRecursively(self, dirname):
|
||||
""" Generates a list of Filename objects: all of the files
|
||||
(not directories) within and below the indicated dirname. """
|
||||
|
|
@ -431,39 +449,66 @@ class PackageInfo:
|
|||
patchMaker.cleanup()
|
||||
return plan
|
||||
|
||||
def __downloadFile(self, step, fileSpec):
|
||||
def __downloadFile(self, step, fileSpec, urlbase = None, filename = None):
|
||||
""" Downloads the indicated file from the host into
|
||||
packageDir. Returns true on success, false on failure. """
|
||||
|
||||
url = self.descFileUrl.rsplit('/', 1)[0]
|
||||
url += '/' + fileSpec.filename
|
||||
url = DocumentSpec(url)
|
||||
print "Downloading package file %s" % (url)
|
||||
|
||||
targetPathname = Filename(self.packageDir, fileSpec.filename)
|
||||
targetPathname.setBinary()
|
||||
|
||||
channel = self.http.makeChannel(False)
|
||||
# TODO: check for a previous partial download, and resume it.
|
||||
targetPathname.unlink()
|
||||
channel.beginGetDocument(url)
|
||||
channel.downloadToFile(targetPathname)
|
||||
while channel.run():
|
||||
step.bytesDone = channel.getBytesDownloaded()
|
||||
self.__updateStepProgress(step)
|
||||
Thread.considerYield()
|
||||
step.bytesDone = channel.getBytesDownloaded()
|
||||
self.__updateStepProgress(step)
|
||||
if not channel.isValid():
|
||||
print "Failed to download %s" % (url)
|
||||
return False
|
||||
if not urlbase:
|
||||
urlbase = self.descFileDirname + '/' + fileSpec.filename
|
||||
|
||||
if not fileSpec.fullVerify(self.packageDir):
|
||||
print "after downloading, %s incorrect" % (
|
||||
fileSpec.filename)
|
||||
return False
|
||||
|
||||
return True
|
||||
# Build up a list of URL's to try downloading from.
|
||||
tryUrls = []
|
||||
if self.host.mirrors:
|
||||
# Choose a mirror at random first, then a different
|
||||
# mirror.
|
||||
mirrors = self.host.mirrors[:]
|
||||
for i in range(2):
|
||||
mirror = random.choice(mirrors)
|
||||
mirrors.remove(mirror)
|
||||
tryUrls.append(mirror + urlbase)
|
||||
if not mirrors:
|
||||
break
|
||||
|
||||
# After trying two mirrors and failing (or if there are no
|
||||
# mirrors), go get it from the original host.
|
||||
url = self.host.hostUrlPrefix + urlbase
|
||||
tryUrls.append(url)
|
||||
|
||||
for url in tryUrls:
|
||||
url = DocumentSpec(url)
|
||||
print "Downloading package file %s" % (url)
|
||||
|
||||
if not filename:
|
||||
filename = fileSpec.filename
|
||||
targetPathname = Filename(self.packageDir, filename)
|
||||
targetPathname.setBinary()
|
||||
|
||||
channel = self.http.makeChannel(False)
|
||||
# TODO: check for a previous partial download, and resume it.
|
||||
targetPathname.makeDir()
|
||||
targetPathname.unlink()
|
||||
channel.beginGetDocument(url)
|
||||
channel.downloadToFile(targetPathname)
|
||||
while channel.run():
|
||||
if step:
|
||||
step.bytesDone = channel.getBytesDownloaded()
|
||||
self.__updateStepProgress(step)
|
||||
Thread.considerYield()
|
||||
if step:
|
||||
step.bytesDone = channel.getBytesDownloaded()
|
||||
self.__updateStepProgress(step)
|
||||
if not channel.isValid():
|
||||
print "Failed to download %s" % (url)
|
||||
continue
|
||||
|
||||
if not fileSpec.fullVerify(self.packageDir, pathname = targetPathname):
|
||||
print "After downloading, %s incorrect" % (url)
|
||||
continue
|
||||
|
||||
return True
|
||||
|
||||
# All mirrors failed.
|
||||
return False
|
||||
|
||||
def __applyPatch(self, step, patchfile):
|
||||
""" Applies the indicated patching in-place to the current
|
||||
|
|
|
|||
|
|
@ -247,10 +247,11 @@ class PatchMaker:
|
|||
""" This is a particular package. This contains all of the
|
||||
information needed to reconstruct the package's desc file. """
|
||||
|
||||
def __init__(self, packageDesc, patchMaker):
|
||||
def __init__(self, packageDesc, patchMaker, xpackage):
|
||||
self.packageDir = Filename(patchMaker.installDir, packageDesc.getDirname())
|
||||
self.packageDesc = packageDesc
|
||||
self.patchMaker = patchMaker
|
||||
self.contentsDocPackage = xpackage
|
||||
self.patchVersion = 1
|
||||
self.currentPv = None
|
||||
self.basePv = None
|
||||
|
|
@ -384,9 +385,9 @@ class PatchMaker:
|
|||
""" Rewrites the desc file with the new patch
|
||||
information. """
|
||||
|
||||
if not self.anyChanges:
|
||||
# No need to rewrite.
|
||||
return
|
||||
## if not self.anyChanges:
|
||||
## # No need to rewrite.
|
||||
## return
|
||||
|
||||
xpackage = self.doc.FirstChildElement('package')
|
||||
if not xpackage:
|
||||
|
|
@ -418,6 +419,13 @@ class PatchMaker:
|
|||
xpackage.InsertEndChild(xpatch)
|
||||
|
||||
self.doc.SaveFile()
|
||||
|
||||
# Now that we've rewritten the xml file, we have to change
|
||||
# the contents.xml file that references it to indicate the
|
||||
# new file hash.
|
||||
fileSpec = FileSpec()
|
||||
fileSpec.fromFile(self.patchMaker.installDir, self.packageDesc)
|
||||
fileSpec.storeXml(self.contentsDocPackage)
|
||||
|
||||
|
||||
def __init__(self, installDir):
|
||||
|
|
@ -440,6 +448,7 @@ class PatchMaker:
|
|||
else:
|
||||
self.processSomePackages(packageNames)
|
||||
|
||||
self.writeContentsFile()
|
||||
self.cleanup()
|
||||
return True
|
||||
|
||||
|
|
@ -481,14 +490,25 @@ class PatchMaker:
|
|||
filename = xpackage.Attribute('filename')
|
||||
if filename and not solo:
|
||||
filename = Filename(filename)
|
||||
package = self.Package(filename, self)
|
||||
package = self.Package(filename, self, xpackage)
|
||||
package.readDescFile()
|
||||
self.packages.append(package)
|
||||
|
||||
xpackage = xpackage.NextSiblingElement('package')
|
||||
|
||||
self.contentsDoc = doc
|
||||
|
||||
return True
|
||||
|
||||
def writeContentsFile(self):
|
||||
""" Writes the contents.xml file at the end of processing. """
|
||||
|
||||
# We trust each of the packages to have already updated their
|
||||
# element within the contents.xml document, so all we have to
|
||||
# do is write out the document.
|
||||
|
||||
self.contentsDoc.SaveFile()
|
||||
|
||||
def getPackageVersion(self, key):
|
||||
""" Returns a shared PackageVersion object for the indicated
|
||||
key. """
|
||||
|
|
|
|||
|
|
@ -1825,10 +1825,7 @@ start_next_download() {
|
|||
|
||||
if (!package->get_ready()) {
|
||||
// This package is ready to download. Begin.
|
||||
string name = package->get_package_display_name();
|
||||
if (name.empty()) {
|
||||
name = package->get_package_name();
|
||||
}
|
||||
string name = package->get_formatted_name();
|
||||
_panda_script_object->set_string_property("downloadPackageName", package->get_package_name());
|
||||
_panda_script_object->set_string_property("downloadPackageDisplayName", name);
|
||||
_panda_script_object->set_int_property("downloadPackageNumber", _download_package_index + 1);
|
||||
|
|
|
|||
|
|
@ -113,10 +113,8 @@ get_package_version() const {
|
|||
////////////////////////////////////////////////////////////////////
|
||||
// Function: P3DPackage::get_package_display_name
|
||||
// Access: Public
|
||||
// Description: Returns the name of this package, for output to the
|
||||
// user. This will be the "public" name of the package,
|
||||
// as formatted for user consumption; it will include
|
||||
// capital letters and spaces where appropriate.
|
||||
// Description: Returns the display_name name of this package, as set
|
||||
// in the desc file.
|
||||
////////////////////////////////////////////////////////////////////
|
||||
inline const string &P3DPackage::
|
||||
get_package_display_name() const {
|
||||
|
|
|
|||
|
|
@ -51,6 +51,7 @@ P3DPackage(P3DHost *host, const string &package_name,
|
|||
if (!_package_version.empty()) {
|
||||
_package_fullname += string(".") + _package_version;
|
||||
}
|
||||
_patch_version = 0;
|
||||
|
||||
// This is set true if the package is a "solo", i.e. a single
|
||||
// file, instead of an xml file and a multifile to unpack.
|
||||
|
|
@ -130,6 +131,34 @@ activate_download() {
|
|||
}
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////
|
||||
// Function: P3DPackage::get_formatted_name
|
||||
// Access: Public
|
||||
// Description: Returns the name of this package, for output to the
|
||||
// user. This will be the "public" name of the package,
|
||||
// as formatted for user consumption; it will include
|
||||
// capital letters and spaces where appropriate.
|
||||
////////////////////////////////////////////////////////////////////
|
||||
string P3DPackage::
|
||||
get_formatted_name() const {
|
||||
ostringstream strm;
|
||||
|
||||
if (!_package_display_name.empty()) {
|
||||
strm << _package_display_name;
|
||||
} else {
|
||||
strm << _package_name;
|
||||
if (!_package_version.empty()) {
|
||||
strm << " " << _package_version;
|
||||
}
|
||||
}
|
||||
|
||||
if (_patch_version != 0) {
|
||||
strm << " rev " << _patch_version;
|
||||
}
|
||||
|
||||
return strm.str();
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////
|
||||
// Function: P3DPackage::add_instance
|
||||
// Access: Public
|
||||
|
|
@ -442,6 +471,8 @@ got_desc_file(TiXmlDocument *doc, bool freshly_downloaded) {
|
|||
TiXmlElement *xcompressed_archive = NULL;
|
||||
|
||||
if (xpackage != NULL) {
|
||||
xpackage->Attribute("patch_version", &_patch_version);
|
||||
|
||||
xuncompressed_archive = xpackage->FirstChildElement("uncompressed_archive");
|
||||
xcompressed_archive = xpackage->FirstChildElement("compressed_archive");
|
||||
|
||||
|
|
|
|||
|
|
@ -57,6 +57,7 @@ public:
|
|||
inline const string &get_package_name() const;
|
||||
inline const string &get_package_version() const;
|
||||
inline const string &get_package_display_name() const;
|
||||
string get_formatted_name() const;
|
||||
inline const TiXmlElement *get_xconfig() const;
|
||||
|
||||
inline const string &get_desc_file_pathname() const;
|
||||
|
|
@ -118,6 +119,7 @@ private:
|
|||
string _package_name;
|
||||
string _package_version;
|
||||
string _package_platform;
|
||||
int _patch_version;
|
||||
string _alt_host;
|
||||
bool _package_solo;
|
||||
string _package_display_name;
|
||||
|
|
|
|||
Loading…
Reference in New Issue