Skip to content

Commit 75965ca

Browse files
linesightclaude
andcommitted
download_cef: add HTTP Range resume support and increase retries
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
1 parent 54d7443 commit 75965ca

1 file changed

Lines changed: 82 additions & 26 deletions

File tree

tools/download_cef.py

Lines changed: 82 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@
3737
import hashlib
3838
import json
3939
import os
40+
import time
4041
import sys
4142
import tarfile
4243
import zipfile
@@ -195,41 +196,96 @@ def find_in_index(cef_version, cef_postfix2):
195196
sys.exit(1)
196197

197198

198-
def download(url, dest_path, expected_size=0):
199-
"""Download url to dest_path with a progress bar."""
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))
206-
try:
207-
response = urlopen(url, timeout=120)
208-
except Exception as exc:
209-
log("ERROR: Download failed: {}".format(exc))
210-
sys.exit(1)
211207

212-
total = int(response.headers.get("Content-Length") or expected_size or 0)
208+
total = expected_size
213209
downloaded = 0
214210
chunk_size = 1024 * 1024 # 1 MB
215211

216-
try:
217-
with open(dest_path, "wb") as fp:
218-
while True:
219-
chunk = response.read(chunk_size)
220-
if not chunk:
221-
break
222-
fp.write(chunk)
223-
downloaded += len(chunk)
224-
_print_progress(downloaded, total)
225-
except Exception as exc:
226-
if os.path.isfile(dest_path):
227-
os.remove(dest_path)
228-
log("\nERROR: Download failed: {}".format(exc))
229-
sys.exit(1)
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+
221+
for attempt in range(1, max_retries + 1):
222+
if attempt > 1:
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+
233+
try:
234+
response = urlopen(req, timeout=120)
235+
except Exception as exc:
236+
if attempt == max_retries:
237+
log("ERROR: Download failed: {}".format(exc))
238+
sys.exit(1)
239+
log("WARNING: Connection error (attempt {}): {}".format(attempt, exc))
240+
time.sleep(2)
241+
continue
230242

231-
print() # end progress line
232-
log("Saved: {}".format(os.path.basename(dest_path)))
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+
258+
error = None
259+
260+
try:
261+
mode = "ab" if downloaded > 0 else "wb"
262+
with open(dest_path, mode) as fp:
263+
while True:
264+
chunk = response.read(chunk_size)
265+
if not chunk:
266+
break
267+
fp.write(chunk)
268+
downloaded += len(chunk)
269+
_print_progress(downloaded, total)
270+
except Exception as exc:
271+
error = exc
272+
273+
print() # end progress line
274+
275+
if error is None and total and downloaded < total:
276+
error = "short read: got {:.1f} MB of {:.1f} MB".format(
277+
downloaded / (1024 * 1024), total / (1024 * 1024))
278+
279+
if error is not None:
280+
if attempt == max_retries:
281+
log("ERROR: Download failed: {}".format(error))
282+
sys.exit(1)
283+
log("WARNING: Download incomplete (attempt {}): {}".format(attempt, error))
284+
time.sleep(2)
285+
continue
286+
287+
log("Saved: {}".format(os.path.basename(dest_path)))
288+
return
233289

234290

235291
def _print_progress(downloaded, total):

0 commit comments

Comments
 (0)