You've already forked MicroPythonOS
mirror of
https://github.com/m5stack/MicroPythonOS.git
synced 2026-05-20 11:51:27 -07:00
DownloadManager cleanups
This commit is contained in:
@@ -64,7 +64,10 @@ class AppStore(Activity):
|
||||
response = await DownloadManager.download_url(json_url)
|
||||
except Exception as e:
|
||||
print(f"Failed to download app index: {e}")
|
||||
self.please_wait_label.set_text(f"Could not download app index from\n{json_url}\nError: {e}")
|
||||
if DownloadManager.is_network_error(e):
|
||||
self.please_wait_label.set_text(f"Network error - check your WiFi connection\nand try again.")
|
||||
else:
|
||||
self.please_wait_label.set_text(f"Could not download app index from\n{json_url}\nError: {e}")
|
||||
return
|
||||
print(f"Got response text: {response[0:20]}")
|
||||
try:
|
||||
@@ -203,6 +206,8 @@ class AppStore(Activity):
|
||||
response = await DownloadManager.download_url(details_url)
|
||||
except Exception as e:
|
||||
print(f"Could not download app details from {details_url}: {e}")
|
||||
if DownloadManager.is_network_error(e):
|
||||
print("Network error while fetching app details")
|
||||
return
|
||||
print(f"Got response text: {response[0:20]}")
|
||||
try:
|
||||
@@ -494,7 +499,10 @@ class AppDetail(Activity):
|
||||
self.progress_bar.set_value(90, True)
|
||||
except Exception as e:
|
||||
print(f"Download failed with exception: {e}")
|
||||
self.install_label.set_text(f"Download failed")
|
||||
if DownloadManager.is_network_error(e):
|
||||
self.install_label.set_text(f"Network error - check WiFi")
|
||||
else:
|
||||
self.install_label.set_text(f"Download failed: {str(e)[:30]}")
|
||||
self.install_button.remove_state(lv.STATE.DISABLED)
|
||||
self.progress_bar.add_flag(lv.obj.FLAG.HIDDEN)
|
||||
self.progress_bar.set_value(0, False)
|
||||
|
||||
@@ -212,7 +212,7 @@ class OSUpdate(Activity):
|
||||
except Exception as e:
|
||||
print(f"show_update_info got exception: {e}")
|
||||
# Check if this is a network connectivity error
|
||||
if self.update_downloader._is_network_error(e):
|
||||
if DownloadManager.is_network_error(e):
|
||||
# Network not available - wait for it to come back
|
||||
print("OSUpdate: Network error while checking for updates, waiting for WiFi")
|
||||
self.set_state(UpdateState.WAITING_WIFI)
|
||||
@@ -461,35 +461,6 @@ class UpdateDownloader:
|
||||
print("UpdateDownloader: Partition module not available, will simulate")
|
||||
self.simulate = True
|
||||
|
||||
def _is_network_error(self, exception):
|
||||
"""Check if exception is a network connectivity error that should trigger pause.
|
||||
|
||||
Args:
|
||||
exception: Exception to check
|
||||
|
||||
Returns:
|
||||
bool: True if this is a recoverable network error
|
||||
"""
|
||||
error_str = str(exception).lower()
|
||||
error_repr = repr(exception).lower()
|
||||
|
||||
# Check for common network error codes and messages
|
||||
# -113 = ECONNABORTED (connection aborted)
|
||||
# -104 = ECONNRESET (connection reset by peer)
|
||||
# -110 = ETIMEDOUT (connection timed out)
|
||||
# -118 = EHOSTUNREACH (no route to host)
|
||||
# -202 = DNS/connection error (network not ready)
|
||||
network_indicators = [
|
||||
'-113', '-104', '-110', '-118', '-202', # Error codes
|
||||
'econnaborted', 'econnreset', 'etimedout', 'ehostunreach', # Error names
|
||||
'connection reset', 'connection aborted', # Error messages
|
||||
'broken pipe', 'network unreachable', 'host unreachable',
|
||||
'failed to download chunk' # From download_manager OSError(-110)
|
||||
]
|
||||
|
||||
return any(indicator in error_str or indicator in error_repr
|
||||
for indicator in network_indicators)
|
||||
|
||||
def _setup_partition(self):
|
||||
"""Initialize the OTA partition for writing."""
|
||||
if not self.simulate and self._current_partition is None:
|
||||
@@ -678,7 +649,7 @@ class UpdateDownloader:
|
||||
result['bytes_written'] = self.bytes_written_so_far
|
||||
result['total_size'] = self.total_size_expected
|
||||
# Check if this is a network error that should trigger pause
|
||||
elif self._is_network_error(e):
|
||||
elif DownloadManager.is_network_error(e):
|
||||
print(f"UpdateDownloader: Network error ({e}), pausing download")
|
||||
|
||||
# Clear buffer - we'll re-download this data on resume
|
||||
|
||||
@@ -11,7 +11,7 @@ adc_pin = None
|
||||
_cached_raw_adc = None
|
||||
_last_read_time = 0
|
||||
CACHE_DURATION_ADC1_MS = 30000 # 30 seconds (cheaper: no WiFi interference)
|
||||
CACHE_DURATION_ADC2_MS = 300000 # 300 seconds (expensive: requires WiFi disable)
|
||||
CACHE_DURATION_ADC2_MS = 600000 # 600 seconds (expensive: requires WiFi disable)
|
||||
#CACHE_DURATION_ADC2_MS = CACHE_DURATION_ADC1_MS # trigger frequent disconnections for debugging OSUpdate resume
|
||||
|
||||
def _is_adc2_pin(pin):
|
||||
|
||||
@@ -14,6 +14,11 @@ Features:
|
||||
- Progress tracking with 2-decimal precision
|
||||
- Download speed reporting
|
||||
- Resume support via Range headers
|
||||
- Network error detection utilities
|
||||
|
||||
Utility Functions:
|
||||
is_network_error(exception) - Check if error is recoverable network error
|
||||
get_resume_position(outfile) - Get file size for resume support
|
||||
|
||||
Example:
|
||||
from mpos import DownloadManager
|
||||
@@ -44,6 +49,19 @@ Example:
|
||||
"https://example.com/stream",
|
||||
chunk_callback=process_chunk
|
||||
)
|
||||
|
||||
# Error handling with retry
|
||||
try:
|
||||
await DownloadManager.download_url(url, outfile="/sdcard/file.bin")
|
||||
except Exception as e:
|
||||
if DownloadManager.is_network_error(e):
|
||||
# Wait and retry with resume
|
||||
await asyncio.sleep(2)
|
||||
resume_from = DownloadManager.get_resume_position("/sdcard/file.bin")
|
||||
headers = {'Range': f'bytes={resume_from}-'} if resume_from > 0 else None
|
||||
await DownloadManager.download_url(url, outfile="/sdcard/file.bin", headers=headers)
|
||||
else:
|
||||
raise # Fatal error
|
||||
"""
|
||||
|
||||
# Constants
|
||||
@@ -174,6 +192,75 @@ async def close_session():
|
||||
_session_lock.release()
|
||||
|
||||
|
||||
def is_network_error(exception):
|
||||
"""Check if exception is a recoverable network error.
|
||||
|
||||
Recognizes common network error codes and messages that indicate
|
||||
temporary connectivity issues that can be retried.
|
||||
|
||||
Args:
|
||||
exception: Exception to check
|
||||
|
||||
Returns:
|
||||
bool: True if this is a network error that can be retried
|
||||
|
||||
Example:
|
||||
try:
|
||||
await DownloadManager.download_url(url)
|
||||
except Exception as e:
|
||||
if DownloadManager.is_network_error(e):
|
||||
# Retry or pause
|
||||
await asyncio.sleep(2)
|
||||
# retry...
|
||||
else:
|
||||
# Fatal error
|
||||
raise
|
||||
"""
|
||||
error_str = str(exception).lower()
|
||||
error_repr = repr(exception).lower()
|
||||
|
||||
# Common network error codes and messages
|
||||
# -113 = ECONNABORTED (connection aborted)
|
||||
# -104 = ECONNRESET (connection reset by peer)
|
||||
# -110 = ETIMEDOUT (connection timed out)
|
||||
# -118 = EHOSTUNREACH (no route to host)
|
||||
# -202 = DNS/connection error (network not ready)
|
||||
network_indicators = [
|
||||
'-113', '-104', '-110', '-118', '-202', # Error codes
|
||||
'econnaborted', 'econnreset', 'etimedout', 'ehostunreach', # Error names
|
||||
'connection reset', 'connection aborted', # Error messages
|
||||
'broken pipe', 'network unreachable', 'host unreachable',
|
||||
'failed to download chunk' # From download_manager OSError(-110)
|
||||
]
|
||||
|
||||
return any(indicator in error_str or indicator in error_repr
|
||||
for indicator in network_indicators)
|
||||
|
||||
|
||||
def get_resume_position(outfile):
|
||||
"""Get the current size of a partially downloaded file.
|
||||
|
||||
Useful for implementing resume functionality with Range headers.
|
||||
|
||||
Args:
|
||||
outfile: Path to file
|
||||
|
||||
Returns:
|
||||
int: File size in bytes, or 0 if file doesn't exist
|
||||
|
||||
Example:
|
||||
resume_from = DownloadManager.get_resume_position("/sdcard/file.bin")
|
||||
if resume_from > 0:
|
||||
headers = {'Range': f'bytes={resume_from}-'}
|
||||
await DownloadManager.download_url(url, outfile=outfile, headers=headers)
|
||||
"""
|
||||
try:
|
||||
import os
|
||||
return os.stat(outfile)[6] # st_size
|
||||
except OSError:
|
||||
return 0
|
||||
|
||||
|
||||
async def download_url(url, outfile=None, total_size=None,
|
||||
progress_callback=None, chunk_callback=None, headers=None,
|
||||
speed_callback=None):
|
||||
|
||||
Reference in New Issue
Block a user