3737import hashlib
3838import json
3939import os
40+ import time
4041import sys
4142import tarfile
4243import zipfile
@@ -195,33 +196,70 @@ def find_in_index(cef_version, cef_postfix2):
195196 sys .exit (1 )
196197
197198
198- def download (url , dest_path , expected_size = 0 , max_retries = 3 ):
199- """Download url to dest_path with a progress bar, retrying on failure ."""
199+ def download (url , dest_path , expected_size = 0 , max_retries = 50 ):
200+ """Download url to dest_path with a progress bar and resume support ."""
200201 try :
201- from urllib .request import urlopen
202+ from urllib .request import Request , urlopen
202203 except ImportError :
203- from urllib2 import urlopen
204+ from urllib2 import Request , urlopen
204205
205206 log ("Downloading: {}" .format (url ))
207+
208+ total = expected_size
209+ downloaded = 0
210+ chunk_size = 1024 * 1024 # 1 MB
211+
212+ # Resume from an existing partial file if present
213+ if os .path .isfile (dest_path ):
214+ downloaded = os .path .getsize (dest_path )
215+ if total and downloaded >= total :
216+ log ("Already fully downloaded: {}" .format (os .path .basename (dest_path )))
217+ return
218+ if downloaded > 0 :
219+ log ("Resuming from {:.1f} MB..." .format (downloaded / (1024 * 1024 )))
220+
206221 for attempt in range (1 , max_retries + 1 ):
207222 if attempt > 1 :
208- log ("Retry {}/{}: {}" .format (attempt , max_retries , url ))
223+ # Re-check how much we have on disk after a failed attempt
224+ if os .path .isfile (dest_path ):
225+ downloaded = os .path .getsize (dest_path )
226+ log ("Retry {}/{} (resuming from {:.1f} MB): {}" .format (
227+ attempt , max_retries , downloaded / (1024 * 1024 ), url ))
228+
229+ req = Request (url )
230+ if downloaded > 0 :
231+ req .add_header ("Range" , "bytes={}-" .format (downloaded ))
232+
209233 try :
210- response = urlopen (url , timeout = 120 )
234+ response = urlopen (req , timeout = 120 )
211235 except Exception as exc :
212236 if attempt == max_retries :
213237 log ("ERROR: Download failed: {}" .format (exc ))
214238 sys .exit (1 )
215239 log ("WARNING: Connection error (attempt {}): {}" .format (attempt , exc ))
240+ time .sleep (2 )
216241 continue
217242
218- total = int (response .headers .get ("Content-Length" ) or expected_size or 0 )
219- downloaded = 0
220- chunk_size = 1024 * 1024 # 1 MB
243+ # Determine total size from response
244+ content_range = response .headers .get ("Content-Range" )
245+ if content_range :
246+ # e.g. "bytes 100-200/614600000"
247+ try :
248+ total = int (content_range .rsplit ("/" , 1 )[- 1 ])
249+ except (ValueError , IndexError ):
250+ pass
251+ else :
252+ total = int (response .headers .get ("Content-Length" ) or total or 0 )
253+ if downloaded > 0 :
254+ # Server didn't honour Range; start over
255+ log ("WARNING: Server ignored Range header, restarting download." )
256+ downloaded = 0
257+
221258 error = None
222259
223260 try :
224- with open (dest_path , "wb" ) as fp :
261+ mode = "ab" if downloaded > 0 else "wb"
262+ with open (dest_path , mode ) as fp :
225263 while True :
226264 chunk = response .read (chunk_size )
227265 if not chunk :
@@ -239,12 +277,11 @@ def download(url, dest_path, expected_size=0, max_retries=3):
239277 downloaded / (1024 * 1024 ), total / (1024 * 1024 ))
240278
241279 if error is not None :
242- if os .path .isfile (dest_path ):
243- os .remove (dest_path )
244280 if attempt == max_retries :
245281 log ("ERROR: Download failed: {}" .format (error ))
246282 sys .exit (1 )
247283 log ("WARNING: Download incomplete (attempt {}): {}" .format (attempt , error ))
284+ time .sleep (2 )
248285 continue
249286
250287 log ("Saved: {}" .format (os .path .basename (dest_path )))
0 commit comments