23
23
import zipfile
24
24
import re
25
25
import time
26
+ from datetime import timedelta
27
+ import functools
28
+
29
+ # Initialize start_time globally
30
+ start_time = - 1
26
31
27
32
if sys .version_info [0 ] == 3 :
28
33
from urllib .request import urlretrieve
@@ -60,26 +65,38 @@ def mkdir_p(path):
60
65
raise
61
66
62
67
def report_progress (block_count , block_size , total_size ):
68
+ global start_time
63
69
downloaded_size = block_count * block_size
64
- current_speed = downloaded_size / (time .time () - start_time )
65
70
time_elapsed = time .time () - start_time
71
+ current_speed = downloaded_size / (time_elapsed )
72
+ elapsed = timedelta (seconds = time_elapsed )
73
+
74
+ hours , remainder = divmod (time_elapsed , 3600 ) # 3600 seconds in an hour
75
+ minutes , remainder = divmod (remainder , 60 )
76
+ seconds , milliseconds = divmod (remainder , 1 )
77
+ formatted_time = "{:02}:{:02}:{:02}.{:03}" .format (int (hours ), int (minutes ), int (seconds ), int (milliseconds * 1000 ))
66
78
67
79
if sys .stdout .isatty ():
68
80
if total_size > 0 :
69
81
percent_complete = min ((downloaded_size / total_size ) * 100 , 100 )
70
- sys .stdout .write (f"\r Downloading... { percent_complete :.2f} % - { downloaded_size / 1024 / 1024 :.2f} MB downloaded - Elapsed Time: { time_elapsed :.2f} seconds - Speed: { current_speed / 1024 / 1024 :.2f} MB/s" )
82
+ sys .stdout .write (f"\r Downloading... { percent_complete :.2f} % - { downloaded_size / 1024 / 1024 :.2f} MB downloaded - Elapsed Time: { formatted_time } - Speed: { current_speed / 1024 / 1024 :.2f} MB/s" )
83
+ else :
84
+ sys .stdout .write (f"\r Downloading... { downloaded_size / 1024 / 1024 :.2f} MB downloaded - Elapsed Time: { formatted_time } - Speed: { current_speed / 1024 / 1024 :.2f} MB/s" )
71
85
sys .stdout .flush ()
72
86
73
87
def format_time (seconds ):
74
88
minutes , seconds = divmod (seconds , 60 )
75
89
return "{:02}:{:05.2f}" .format (int (minutes ), seconds )
76
90
91
+ def print_verification_progress (total_files , i , t1 ):
92
+ if sys .stdout .isatty ():
93
+ sys .stdout .write (f'\r Elapsed time { format_time (time .time () - t1 )} ' )
94
+ sys .stdout .flush ()
95
+
77
96
def verify_files (filename , destination , rename_to ):
78
97
# Set the path of the extracted directory
79
98
extracted_dir_path = destination
80
-
81
99
t1 = time .time ()
82
-
83
100
if filename .endswith (".zip" ):
84
101
try :
85
102
with zipfile .ZipFile (filename , 'r' ) as archive :
@@ -88,15 +105,12 @@ def verify_files(filename, destination, rename_to):
88
105
for i , zipped_file in enumerate (archive .namelist (), 1 ):
89
106
local_path = os .path .join (extracted_dir_path , zipped_file .replace (first_dir , rename_to , 1 ))
90
107
if not os .path .exists (local_path ):
91
- print (f'\n Missing { zipped_file } on location: { extracted_dir_path } ' )
108
+ # print(f'\nMissing {zipped_file} on location: {extracted_dir_path}')
92
109
print (f"\n Verification failed; aborted in { format_time (time .time () - t1 )} " )
93
110
return False
94
- #print(f'\rVerification progress: {i/total_files*100:.2f}%', end='')
95
- if sys .stdout .isatty ():
96
- sys .stdout .write (f'\r Verification progress: { i / total_files * 100 :.2f} %' )
97
- sys .stdout .flush ()
111
+ print_verification_progress (total_files , i , t1 )
98
112
except zipfile .BadZipFile :
99
- print (f"\n Verification failed; aborted in { format_time (time .time () - t1 )} " )
113
+ print (f"Verification failed; aborted in { format_time (time .time () - t1 )} " )
100
114
return False
101
115
elif filename .endswith (".tar.gz" ):
102
116
try :
@@ -106,15 +120,12 @@ def verify_files(filename, destination, rename_to):
106
120
for i , zipped_file in enumerate (archive .getnames (), 1 ):
107
121
local_path = os .path .join (extracted_dir_path , zipped_file .replace (first_dir , rename_to , 1 ))
108
122
if not os .path .exists (local_path ):
109
- print (f'\n Missing { zipped_file } on location: { extracted_dir_path } ' )
110
- print (f"\n Verification failed; aborted in { format_time (time .time () - t1 )} " )
123
+ # print(f'\nMissing {zipped_file} on location: {extracted_dir_path}')
124
+ print (f"Verification failed; aborted in { format_time (time .time () - t1 )} " )
111
125
return False
112
- #print(f'\rVerification progress: {i/total_files*100:.2f}%', end='')
113
- if sys .stdout .isatty ():
114
- sys .stdout .write (f'\r Verification progress: { i / total_files * 100 :.2f} %' )
115
- sys .stdout .flush ()
126
+ print_verification_progress (total_files , i , t1 )
116
127
except tarfile .ReadError :
117
- print (f"\n Verification failed; aborted in { format_time (time .time () - t1 )} " )
128
+ print (f"Verification failed; aborted in { format_time (time .time () - t1 )} " )
118
129
return False
119
130
elif filename .endswith (".tar.xz" ):
120
131
try :
@@ -124,63 +135,86 @@ def verify_files(filename, destination, rename_to):
124
135
for i , zipped_file in enumerate (archive .getnames (), 1 ):
125
136
local_path = os .path .join (extracted_dir_path , zipped_file .replace (first_dir , rename_to , 1 ))
126
137
if not os .path .exists (local_path ):
127
- print (f'\n Missing { zipped_file } on location: { extracted_dir_path } ' )
128
- print (f"\n Verification failed; aborted in { format_time (time .time () - t1 )} " )
138
+ # print(f'\nMissing {zipped_file} on location: {extracted_dir_path}')
139
+ print (f"Verification failed; aborted in { format_time (time .time () - t1 )} " )
129
140
return False
130
- #print(f'\rVerification progress: {i/total_files*100:.2f}%', end='')
131
- if sys .stdout .isatty ():
132
- sys .stdout .write (f'\r Verification progress: { i / total_files * 100 :.2f} %' )
133
- sys .stdout .flush ()
141
+ print_verification_progress (total_files , i , t1 )
134
142
except tarfile .ReadError :
135
- print (f"\n Verification failed; aborted in { format_time (time .time () - t1 )} " )
143
+ print (f"Verification failed; aborted in { format_time (time .time () - t1 )} " )
136
144
return False
137
145
else :
138
146
raise NotImplementedError ('Unsupported archive type' )
139
147
140
- if (verobse ):
141
- print (f"\n Verification passed; completed in { format_time (time .time () - t1 )} " )
142
-
148
+ #if(verbose):
149
+ #print(f"\nVerification passed; completed in {format_time(time.time() - t1)}")
143
150
return True
144
151
145
- def unpack (filename , destination ):
152
+
153
+ def unpack (filename , destination , force_extract ):
146
154
dirname = ''
147
- print (' > Verify... ' )
155
+ file_is_corrupted = False
156
+ if (not force_extract ):
157
+ print (f' > Verify... ' , end = "" , flush = True )
148
158
149
- if filename .endswith ('tar.gz' ):
150
- tfile = tarfile .open (filename , 'r:gz' )
151
- dirname = tfile .getnames ()[0 ]
152
- elif filename .endswith ('tar.xz' ):
153
- tfile = tarfile .open (filename , 'r:xz' )
154
- dirname = tfile .getnames ()[0 ]
155
- elif filename .endswith ('zip' ):
156
- zfile = zipfile .ZipFile (filename )
157
- dirname = zfile .namelist ()[0 ]
158
- else :
159
- raise NotImplementedError ('Unsupported archive type' )
159
+ try :
160
+ if filename .endswith ('tar.gz' ):
161
+ if tarfile .is_tarfile (filename ):
162
+ tfile = tarfile .open (filename , 'r:gz' )
163
+ dirname = tfile .getnames ()[0 ]
164
+ else :
165
+ print ('File corrupted!' )
166
+ file_is_corrupted = True
167
+ elif filename .endswith ('tar.xz' ):
168
+ if tarfile .is_tarfile (filename ):
169
+ tfile = tarfile .open (filename , 'r:xz' )
170
+ dirname = tfile .getnames ()[0 ]
171
+ else :
172
+ print ('File corrupted!' )
173
+ file_is_corrupted = True
174
+ elif filename .endswith ('zip' ):
175
+ if zipfile .is_zipfile (filename ):
176
+ zfile = zipfile .ZipFile (filename )
177
+ dirname = zfile .namelist ()[0 ]
178
+ else :
179
+ print ('File corrupted!' )
180
+ file_is_corrupted = True
181
+ else :
182
+ raise NotImplementedError ('Unsupported archive type' )
183
+ except EOFError :
184
+ print (f'File corrupted or incomplete!' )
185
+ file_is_corrupted = True
186
+
187
+ if (file_is_corrupted ):
188
+ corrupted_filename = filename + ".corrupted"
189
+ os .rename (filename , corrupted_filename )
190
+ if (verbose ):
191
+ print (f'Renaming corrupted archive to { corrupted_filename } ' )
192
+ return False
160
193
161
194
# A little trick to rename tool directories so they don't contain version number
162
195
rename_to = re .match (r'^([a-z][^\-]*\-*)+' , dirname ).group (0 ).strip ('-' )
163
196
if rename_to == dirname and dirname .startswith ('esp32-arduino-libs-' ):
164
197
rename_to = 'esp32-arduino-libs'
165
198
166
- if (verify_files (filename , destination , rename_to )):
167
- print (" Files ok. Skipping Extraction" )
168
- return
199
+ if not force_extract :
200
+ now = time .time ()
201
+ if (verify_files (filename , destination , rename_to , now )):
202
+ print (" Files ok. Skipping Extraction" )
203
+ return True
204
+ else :
205
+ print (" Failed. extracting" )
169
206
else :
170
- print (" Failed. extracting " )
207
+ print (" Forcing extraction " )
171
208
172
209
if filename .endswith ('tar.gz' ):
173
210
tfile = tarfile .open (filename , 'r:gz' )
174
211
tfile .extractall (destination )
175
- dirname = tfile .getnames ()[0 ]
176
212
elif filename .endswith ('tar.xz' ):
177
213
tfile = tarfile .open (filename , 'r:xz' )
178
214
tfile .extractall (destination )
179
- dirname = tfile .getnames ()[0 ]
180
215
elif filename .endswith ('zip' ):
181
216
zfile = zipfile .ZipFile (filename )
182
217
zfile .extractall (destination )
183
- dirname = zfile .namelist ()[0 ]
184
218
else :
185
219
raise NotImplementedError ('Unsupported archive type' )
186
220
@@ -214,7 +248,7 @@ def download_file_with_progress(url,filename):
214
248
block_count += 1
215
249
report_progress (block_count , block_size , total_size )
216
250
else :
217
- raise Exception ( 'nonexisting file or connection error' )
251
+ raise Exception ( 'Non-existing file or connection error' )
218
252
219
253
def download_file (url ,filename ):
220
254
import ssl
@@ -234,15 +268,20 @@ def download_file(url,filename):
234
268
break
235
269
out_file .write (block )
236
270
else :
237
- raise Exception ('nonexisting file or connection error' )
271
+ raise Exception ('Non-existing file or connection error' )
238
272
239
- def get_tool (tool ):
273
+ def get_tool (tool , force_download , force_extract ):
274
+ global start_time
240
275
sys_name = platform .system ()
241
276
archive_name = tool ['archiveFileName' ]
242
277
local_path = dist_dir + archive_name
243
278
url = tool ['url' ]
244
- if not os .path .isfile (local_path ):
245
- print ('Downloading ' + archive_name + ' ...' )
279
+ start_time = time .time ()
280
+ if not os .path .isfile (local_path ) or force_download :
281
+ if verbose :
282
+ print ('Downloading \' ' + archive_name + '\' to \' ' + local_path + '\' ' )
283
+ else :
284
+ print ('Downloading \' ' + archive_name + '\' ...' )
246
285
sys .stdout .flush ()
247
286
if 'CYGWIN_NT' in sys_name :
248
287
import ssl
@@ -264,12 +303,12 @@ def get_tool(tool):
264
303
urlretrieve (url , local_path , report_progress )
265
304
except :
266
305
download_file_with_progress (url , local_path )
267
- sys .stdout .write ("\r Done \n " )
306
+ sys .stdout .write (" - Done \n " )
268
307
sys .stdout .flush ()
269
308
else :
270
309
print ('Tool {0} already downloaded' .format (archive_name ))
271
310
sys .stdout .flush ()
272
- unpack (local_path , '.' )
311
+ return unpack (local_path , '.' , force_extract )
273
312
274
313
def load_tools_list (filename , platform ):
275
314
tools_info = json .load (open (filename ))['packages' ][0 ]['tools' ]
@@ -312,11 +351,34 @@ def identify_platform():
312
351
print ('System: %s, Bits: %d, Info: %s' % (sys_name , bits , sys_platform ))
313
352
return arduino_platform_names [sys_name ][bits ]
314
353
354
+ def print_help ():
355
+ print ("TODO help" )
356
+
315
357
if __name__ == '__main__' :
358
+ option_print_help = "-h" in sys .argv
316
359
verbose = "-v" in sys .argv
317
- is_test = (len (sys .argv ) > 1 and sys .argv [1 ] == '-h' )
360
+ force_download = "-d" in sys .argv
361
+ force_extract = "-e" in sys .argv
362
+ force_all = "-f" in sys .argv
363
+ is_test = "-t" in sys .argv
364
+
365
+ print (f'Debug: options values: option_print_help={ option_print_help } ; verbose={ verbose } ; force_download={ force_download } ; force_extract={ force_extract } ; force_all={ force_all } ; is_test={ is_test } ;' )
366
+ if option_print_help :
367
+ print_help ()
368
+ sys .exit ()
369
+
370
+ if is_test and (force_download or force_extract or force_all ):
371
+ print ('Cannot combine test (-t) and forced execution (-d | -e | -f)' )
372
+ print_help ()
373
+ sys .exit ()
374
+
318
375
if is_test :
319
376
print ('Test run!' )
377
+
378
+ if force_all :
379
+ force_download = True
380
+ force_extract = True
381
+
320
382
identified_platform = identify_platform ()
321
383
print ('Platform: {0}' .format (identified_platform ))
322
384
tools_to_download = load_tools_list (current_dir + '/../package/package_esp32_index.template.json' , identified_platform )
@@ -325,5 +387,9 @@ def identify_platform():
325
387
if is_test :
326
388
print ('Would install: {0}' .format (tool ['archiveFileName' ]))
327
389
else :
328
- get_tool (tool )
390
+ if (not get_tool (tool , force_download , force_extract )):
391
+ if (verbose ):
392
+ print (f"Tool { tool ['archiveFileName' ]} was corrupted. Re-downloading...\n " )
393
+ if (not get_tool (tool , force_download , force_extract )): # Corrupted file was renamed, will not be found and will be re-downloaded
394
+ print (f"Tool { tool } was corrupted, but re-downloading did not help!\n " )
329
395
print ('Platform Tools Installed' )
0 commit comments