-
Notifications
You must be signed in to change notification settings - Fork 8
/
updateClient.py
220 lines (182 loc) · 6.62 KB
/
updateClient.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
# -*- coding: utf-8 -*-
# Screaming Strike update checker / downloader client
# Copyright (C) 2019 Yukio Nozawa <[email protected]>
# License: GPL V2.0 (See copying.txt for details)
import platform
import threading
import urllib.request
import bgtsound
import globalVars
"""This module contains UpdateChecker class, which can be used as update checking client. It also contains updateDownloader."""
RET_USING_LATEST = 1
RET_NEW_VERSION_AVAILABLE = 2
RET_NOT_SUPPORTED = 0
RET_CONNECTION_ERROR = -1
RET_NEVER_CHECKED = -2
class Checker(object):
"""This object checks for updates"""
def __init__(self):
self.version = None # Version retrieved from server
self.serverAddress = "" # https://www.yourhost.com/screamingstrike/updateChecker.php
self.localVersion = 1.00 # Default
self.lastResult = RET_NEVER_CHECKED
self.lastError = ""
self.working = False
self.thread = None
def initialize(self, version, address):
"""Initializes this client with the given local version and server address. If working thread is active, this method does nothing.
:param version: Current local version.
:type version: float
:param address: Server address. It should point to updateChecker.php.
:type address: str
"""
if self.working:
return
self.serverAddress = address
self.currentVersion = version
def run(self):
"""Runs the update check as a background thread. If it's already checking updates, this method does nothing. If the server URL is not set, it sets the last result to RET_NOT_SUPPORTED."""
if self.working:
return
self.thread = threading.Thread(target=self._thread)
self.thread.setDaemon(True)
self.thread.start()
def _thread(self):
"""Internal thread method. You should not call this method."""
self.working = True
if self.serverAddress == "":
self.lastResult = RET_NOT_SUPPORTED
self.working = False
return
# end URL not suplied
req = urllib.request.Request(self.serverAddress + "?updatecheck=true&platform=" + platform.system())
try:
with urllib.request.urlopen(req) as res:
body = res.read()
# end with
except urllib.error.URLError as r:
self.lastResult = RET_CONNECTION_ERROR
self.lastError = str(r)
self.working = False
return
# end except
body = body.decode()
self.version = float(body)
self.lastResult = RET_NEW_VERSION_AVAILABLE if self.version > self.currentVersion else RET_USING_LATEST
self.lastError = ""
self.working = False
return
# end _thread
def wait(self):
"""Blocks until the update checking finishes."""
if self.thread:
self.thread.join()
def getLastResult(self):
"""Retrieves the result of last checked update. Returns RET_** values.
:rtype: int
"""
return self.lastResult
# end getLastResult
def getLastError(self):
"""Retrieves the last error string.
:rtype: str
"""
return self.lastError
def getVersion(self):
"""Returns the version number retrieved from the server.
:rtype: float
"""
return self.version
class Downloader(object):
"""This object downloads the specified file as background thread."""
def __init__(self):
self.thread = None
self.url = None
self.localname = None
self.completeCallback = None
self.working = False
self.succeeded = False
self.lastError = None
def initialize(self, url, localname, completeCallback):
"""Initializes the downloader.
:param url: URL.
:type url: str
:param localname: Local file name.
:type localname: str
:param completeCallback: Callback when download finished.
:type completeCallback: callable
"""
if self.working:
return
self.url = url
self.localname = localname
self.completeCallback = completeCallback
self.percentage = 0
self.totalSize = 0
self.receivedSize = 0
def run(self):
"""Starts the update"""
if not self.url:
return
if not self.localname:
return
self.thread = threading.Thread(target=self._thread)
self.thread.setDaemon(True)
self.thread.start()
def _progress(self, blockCount, blockSize, totalSize):
"""Internal progress callback. You should not call this method."""
self.receivedSize = blockCount * blockSize
self.totalSize = totalSize # Overwriting unnecessarily, but don't care
self.percentage = 100.0 * self.receivedSize / self.totalSize
def _thread(self):
"""Internal thread. You should not call this method."""
self.working = True
try:
urllib.request.urlretrieve(url=self.url, filename=self.localname, reporthook=self._progress)
except urllib.error.URLError as r:
self.succeeded = False
self.working = False
self.lastError = str(r)
if self.completeCallback:
self.completeCallback()
return
# end except
self.succeeded = True
if self.completeCallback:
self.completeCallback()
self.working = False
def hasSucceeded(self):
"""Retrieves if the download has succeeded.
:rtype: bool
"""
return self.succeeded
def getPercentage(self):
"""Gets the percentage of download.
:rtype: float
"""
return self.percentage
def getReceivedSize(self):
"""Gets the total received size so far.
:rtype: int
"""
return self.receivedSize
def getTotalSize(self):
"""Gets the total size of download.
:rtype: int
"""
return self.totalSize
def isWorking(self):
"""Retrieves if the download is in progress.
:rtype: bool
"""
return self.working
def getLocalName(self):
"""Retrieves the local file name.
:rtype: str
"""
return self.localname
def getLastError(self):
"""Retrieves the last error.
:rtype: str
"""
return self.lastError