189 lines
6.8 KiB
Python
189 lines
6.8 KiB
Python
import json, sys, requests, time, os, platform, traceback
|
|
from io import BytesIO
|
|
|
|
from PyQt4 import QtGui, QtCore
|
|
|
|
from game_version import *
|
|
|
|
returncode = -1
|
|
assetfile = {
|
|
"Windows": "AO2XP-Windows.zip",
|
|
"Darwin": "AO2XP-macOS.zip",
|
|
"Linux": "AO2XP-Linux.tar.gz"
|
|
}
|
|
|
|
def checkForUpdates(force=False):
|
|
update_dialog = QtGui.QProgressDialog()
|
|
update_dialog.setWindowTitle("AO2XP updater")
|
|
update_dialog.setAutoClose(False)
|
|
update_dialog.setAutoReset(False)
|
|
update_dialog.setLabelText("Checking for client updates...")
|
|
update_dialog.resize(512, 96)
|
|
update_dialog.setMinimum(0)
|
|
update_dialog.setMaximum(0)
|
|
update_dialog.setValue(0)
|
|
|
|
thr = updateThread(update_dialog, force)
|
|
|
|
def setProgressValue(value):
|
|
update_dialog.setValue(value)
|
|
def setLabelText(msg):
|
|
update_dialog.setLabelText(msg)
|
|
def showMessageBox(icon, title, msg):
|
|
getattr(QtGui.QMessageBox, str(icon))(None, title, msg)
|
|
def updateAvailableCall(name, version, body):
|
|
updateWindow = updateAvailableWidget(name, version, body)
|
|
result = updateWindow.show()
|
|
thr.waitConfirm = 1 if result else -1
|
|
if not result:
|
|
update_dialog.close()
|
|
returncode = -2
|
|
else:
|
|
update_dialog.setMinimum(0)
|
|
update_dialog.setMaximum(100)
|
|
|
|
thr.progressValue.connect(setProgressValue)
|
|
thr.labelText.connect(setLabelText)
|
|
thr.showMessageBox.connect(showMessageBox)
|
|
thr.updateAvailable.connect(updateAvailableCall)
|
|
thr.finished.connect(update_dialog.close)
|
|
thr.start()
|
|
|
|
update_dialog.exec_()
|
|
if not update_dialog.wasCanceled() and thr.isRunning(): thr.wait()
|
|
|
|
print "update return code:", returncode
|
|
return returncode
|
|
|
|
class updateAvailableWidget(QtGui.QDialog):
|
|
def __init__(self, title, version, body):
|
|
super(self.__class__, self).__init__()
|
|
self.layout = QtGui.QVBoxLayout(self)
|
|
self.layout.setAlignment(QtCore.Qt.AlignTop)
|
|
|
|
self.available = QtGui.QLabel(text="Changelog for update \"%s\":" % title)
|
|
self.changelog = QtGui.QTextEdit()
|
|
self.confirmtext = QtGui.QLabel(text="Do you want to download and install the update?")
|
|
self.yesnolayout = QtGui.QHBoxLayout()
|
|
self.yes = QtGui.QPushButton(text="Yes")
|
|
self.no = QtGui.QPushButton(text="No")
|
|
|
|
self.setModal(True)
|
|
self.setWindowTitle("Update %s available" % version)
|
|
self.changelog.setText(body)
|
|
self.changelog.setReadOnly(True)
|
|
self.yes.clicked.connect(self.confirm)
|
|
self.no.clicked.connect(self.cancel)
|
|
|
|
self.yesnolayout.addWidget(self.yes)
|
|
self.yesnolayout.addWidget(self.no)
|
|
self.layout.addWidget(self.available)
|
|
self.layout.addWidget(self.changelog)
|
|
self.layout.addWidget(self.confirmtext)
|
|
self.layout.addLayout(self.yesnolayout, QtCore.Qt.AlignBottom)
|
|
self.result = False
|
|
|
|
self.resize(640, self.sizeHint().height())
|
|
|
|
def confirm(self):
|
|
self.close()
|
|
self.result = True
|
|
|
|
def cancel(self):
|
|
self.close()
|
|
self.result = False
|
|
|
|
def show(self):
|
|
super(self.__class__, self).show()
|
|
self.exec_()
|
|
return self.result
|
|
|
|
class updateThread(QtCore.QThread):
|
|
progressValue = QtCore.pyqtSignal(int)
|
|
labelText = QtCore.pyqtSignal(str)
|
|
showMessageBox = QtCore.pyqtSignal(str, str, str)
|
|
updateAvailable = QtCore.pyqtSignal(str, str, str)
|
|
finished = QtCore.pyqtSignal()
|
|
waitConfirm = 0
|
|
|
|
def __init__(self, nouis, force):
|
|
super(self.__class__, self).__init__()
|
|
self.jm = nouis
|
|
self.force = force
|
|
|
|
def run(self):
|
|
global returncode
|
|
|
|
try:
|
|
manifest = json.load(BytesIO(requests.get("http://api.github.com/repos/headshot2017/AO2XP/releases").content))
|
|
except:
|
|
print traceback.format_exc()
|
|
self.showMessageBox.emit("critical", "Error", "Failed to check for updates.\nPlease check your internet connection.")
|
|
self.finished.emit()
|
|
return
|
|
|
|
if self.jm.wasCanceled():
|
|
return
|
|
|
|
if GAME_VERSION != manifest[0]["tag_name"] or self.force: # update available
|
|
self.updateAvailable.emit(manifest[0]["name"], manifest[0]["tag_name"], manifest[0]["body"])
|
|
self.waitConfirm = 0
|
|
|
|
while self.waitConfirm == 0: pass
|
|
|
|
if self.waitConfirm > 0:
|
|
resume_bytes = 0
|
|
filename = assetfile[platform.system()]
|
|
link = ""
|
|
for asset in manifest[0]["assets"]:
|
|
if asset["name"] == filename:
|
|
link = asset["browser_download_url"]
|
|
|
|
if not link:
|
|
self.showMessageBox.emit("critical", "Error", "Release %s is missing the '%s' file.\nCannot continue updating." % (manifest[0]["tag_name"], filename))
|
|
self.finished.emit()
|
|
return
|
|
|
|
updatezip = "update" + os.path.splitext(filename)[1]
|
|
if not os.path.exists(updatezip):
|
|
downloadfile = open(updatezip, "wb")
|
|
else:
|
|
existing_data = open(updatezip, "rb").read()
|
|
downloadfile = open(updatezip, "ab")
|
|
resume_bytes = len(existing_data)
|
|
print resume_bytes
|
|
del existing_data
|
|
|
|
self.labelText.emit("Downloading '%s' for update '%s'..." % (filename, manifest[0]["name"]))
|
|
dl = resume_bytes
|
|
speed = 0.0
|
|
start = time.clock()
|
|
calcspeed_time = time.time()
|
|
zip = requests.get(link, stream=True, headers={"Range": "bytes=%d-" % resume_bytes})
|
|
length = resume_bytes + int(zip.headers.get("content-length"))
|
|
|
|
for noby in zip.iter_content(chunk_size=4096):
|
|
if not self.jm.isVisible():
|
|
downloadfile.close()
|
|
return
|
|
|
|
downloadfile.write(noby)
|
|
dl += len(noby)
|
|
percent = 100 * dl / length
|
|
if percent != self.jm.value():
|
|
self.progressValue.emit(percent)
|
|
self.labelText.emit("Downloading '%s' for update '%s'... %.1f KB/s" % (filename, manifest[0]["name"], speed))
|
|
|
|
if (time.time() - calcspeed_time) >= 0.5:
|
|
calcspeed_time = time.time()
|
|
speed = ((dl-resume_bytes)/(time.clock() - start)) / 1024.
|
|
self.labelText.emit("Downloading '%s' for update '%s'... %.1f KB/s" % (filename, manifest[0]["name"], speed))
|
|
|
|
print "downloaded update"
|
|
downloadfile.close()
|
|
returncode = 0
|
|
else:
|
|
returncode = -2
|
|
|
|
self.finished.emit()
|