3636import sys
3737import time # noqa: F401
3838from html .parser import HTMLParser
39+ from urllib .parse import quote
3940
4041import requests
4142
4445parser .add_argument ("--ardupilotRepoFolder" , dest = 'gitFolder' , default = "../ardupilot" , help = "Ardupilot git folder. " )
4546parser .add_argument ("--destination" , dest = 'destFolder' , default = "../../../../new_params_mversion" , help = "Parameters*.rst destination folder." ) # noqa: E501
4647parser .add_argument ('--vehicle' , dest = 'single_vehicle' , help = "If you just want to copy to one vehicle, you can do this. Otherwise it will work for all vehicles (Copter, Plane, Rover, AntennaTracker, Sub, Blimp)" ) # noqa: E501
48+
49+ DEFAULT_CACHE_TIME = 6 * 3600
50+
51+ # Get the directory where this script is located
52+ script_dir = os .path .dirname (os .path .abspath (__file__ ))
53+ default_http_request_cache_dir = os .path .join (script_dir , '.cache' )
54+
55+ parser .add_argument ("--cache-dir" , dest = 'cache_dir' , default = default_http_request_cache_dir ,
56+ help = "Directory to cache HTTP responses" )
4757args = parser .parse_args ()
4858
4959
@@ -94,6 +104,102 @@ def format(self, record):
94104})
95105
96106
107+ def fetch_url_with_cache (url , cache_dir = None ):
108+ """Fetch URL content with caching to avoid repeated downloads."""
109+ if cache_dir is None :
110+ cache_dir = args .cache_dir
111+
112+ os .makedirs (cache_dir , exist_ok = True )
113+
114+ # Create cache filename from URL
115+ cache_filename = quote (url , safe = '' ) + '.cache'
116+ cache_path = os .path .join (cache_dir , cache_filename )
117+ cache_meta_path = cache_path + '.meta'
118+
119+ def load_cached_content ():
120+ with open (cache_path , 'r' , encoding = 'utf-8' ) as f :
121+ return f .read ()
122+
123+ def load_cache_metadata ():
124+ if not os .path .exists (cache_meta_path ):
125+ return {}
126+ try :
127+ with open (cache_meta_path , 'r' , encoding = 'utf-8' ) as f :
128+ return json .load (f )
129+ except OSError :
130+ return {}
131+
132+ def save_cache (content , response ):
133+ with open (cache_path , 'w' , encoding = 'utf-8' ) as f :
134+ f .write (content )
135+
136+ metadata = {}
137+ etag = response .headers .get ('ETag' )
138+ last_modified = response .headers .get ('Last-Modified' )
139+ if etag :
140+ metadata ['etag' ] = etag
141+ if last_modified :
142+ metadata ['last_modified' ] = last_modified
143+ if metadata :
144+ with open (cache_meta_path , 'w' , encoding = 'utf-8' ) as f :
145+ json .dump (metadata , f )
146+
147+ def refresh_cache_mtime ():
148+ try :
149+ os .utime (cache_path , None )
150+ except OSError :
151+ pass
152+
153+ if os .path .exists (cache_path ):
154+ cache_age = time .time () - os .path .getmtime (cache_path )
155+ if cache_age < DEFAULT_CACHE_TIME :
156+ debug (f"Using cached content for { url } " )
157+ return load_cached_content ()
158+
159+ cache_metadata = {}
160+ headers = {}
161+ if os .path .exists (cache_path ):
162+ cache_metadata = load_cache_metadata ()
163+ if cache_metadata .get ('etag' ):
164+ headers ['If-None-Match' ] = cache_metadata ['etag' ]
165+ if cache_metadata .get ('last_modified' ):
166+ headers ['If-Modified-Since' ] = cache_metadata ['last_modified' ]
167+
168+ if headers :
169+ try :
170+ debug (f"HEAD checking server for { url } " )
171+ head_response = session .head (url , timeout = 30 , headers = headers , allow_redirects = True )
172+ if head_response .status_code == 304 :
173+ debug (f"Cache still valid for { url } " )
174+ refresh_cache_mtime ()
175+ return load_cached_content ()
176+
177+ head_response .raise_for_status ()
178+ if (head_response .headers .get ('ETag' ) == cache_metadata .get ('etag' ) and
179+ head_response .headers .get ('Last-Modified' ) == cache_metadata .get ('last_modified' )):
180+ debug (f"Server metadata unchanged for { url } , using local cache" )
181+ refresh_cache_mtime ()
182+ return load_cached_content ()
183+ except requests .RequestException as e :
184+ debug (f"HEAD request failed for { url } : { e } " )
185+ # Fallback to GET if the HEAD request is unsupported or fails.
186+
187+ try :
188+ debug (f"Fetching full content from { url } " )
189+ response = session .get (url , timeout = 30 , allow_redirects = True )
190+ response .raise_for_status ()
191+ content = response .text
192+ except requests .RequestException as e :
193+ error (f"Failed to fetch { url } : { e } " )
194+ if os .path .exists (cache_path ):
195+ debug (f"Using stale cached content for { url } " )
196+ return load_cached_content ()
197+ raise
198+
199+ save_cache (content , response )
200+ return content
201+
202+
97203def run_git (cmd , cwd = None , check = True , max_retries = 3 ):
98204 """Run git command with retry logic for lock conflicts"""
99205 if cwd is None :
@@ -406,9 +512,7 @@ def handle_starttag(self, tag, attrs):
406512 try :
407513 debug (f"Fetching { firmware_url } { vehicle } " )
408514
409- response = session .get (firmware_url + vehicle , timeout = 30 )
410- response .raise_for_status ()
411- content = response .text
515+ content = fetch_url_with_cache (firmware_url + vehicle )
412516 html_parser .feed (content )
413517 except Exception as e :
414518 error (f"Vehicles folders list download error: { e } " )
@@ -460,9 +564,7 @@ def handle_starttag(self, tag, attrs):
460564 try :
461565 debug (f"Fetching { url } " )
462566
463- response = session .get (url , timeout = 30 )
464- response .raise_for_status ()
465- content = response .text
567+ content = fetch_url_with_cache (url )
466568 html_parser .feed (content )
467569 except Exception as e :
468570 error (f"Board folders list download error: { e } " )
@@ -483,11 +585,9 @@ def fetch_commit_hash(version_link, board, file):
483585 progress (f"Processing link...\t { fetch_link } " )
484586
485587 try :
486- response = session .get (fetch_link , timeout = 30 )
487- response .raise_for_status ()
488- fecth_response = response .text
588+ fetch_response = fetch_url_with_cache (fetch_link )
489589
490- commit_details = fecth_response .split ("\n " )
590+ commit_details = fetch_response .split ("\n " )
491591 commit_hash = commit_details [0 ][7 :]
492592 # version = commit_details[6] the sizes cary
493593 version = commit_details .pop (- 2 )
0 commit comments