Skip to content

Commit b295b3b

Browse files
Update docs
1 parent 1dc6ab3 commit b295b3b

File tree

1 file changed

+112
-31
lines changed

1 file changed

+112
-31
lines changed

docs/frameworks/download-manager.md

Lines changed: 112 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -164,7 +164,70 @@ success = await DownloadManager.download_url(
164164
)
165165
```
166166

167-
### Helper Functions
167+
### Utility Functions
168+
169+
#### `DownloadManager.is_network_error(exception)`
170+
171+
Check if an exception is a recoverable network error.
172+
173+
Recognizes common network error codes and messages that indicate temporary connectivity issues that can be retried.
174+
175+
**Parameters:**
176+
- `exception` - Exception to check
177+
178+
**Returns:**
179+
- `bool` - True if this is a network error that can be retried
180+
181+
**Example:**
182+
```python
183+
try:
184+
await DownloadManager.download_url(url)
185+
except Exception as e:
186+
if DownloadManager.is_network_error(e):
187+
# Network error - wait and retry
188+
await TaskManager.sleep(2)
189+
# Retry with resume...
190+
else:
191+
# Fatal error - show error to user
192+
raise
193+
```
194+
195+
**Detected error codes:**
196+
- `-110` - ETIMEDOUT (connection timed out)
197+
- `-113` - ECONNABORTED (connection aborted)
198+
- `-104` - ECONNRESET (connection reset by peer)
199+
- `-118` - EHOSTUNREACH (no route to host)
200+
- `-202` - DNS/connection error (network not ready)
201+
202+
**Detected error messages:**
203+
- "connection reset", "connection aborted"
204+
- "broken pipe", "network unreachable", "host unreachable"
205+
- "failed to download chunk"
206+
207+
#### `DownloadManager.get_resume_position(outfile)`
208+
209+
Get the current size of a partially downloaded file.
210+
211+
Useful for implementing resume functionality with Range headers.
212+
213+
**Parameters:**
214+
- `outfile` - Path to file
215+
216+
**Returns:**
217+
- `int` - File size in bytes, or 0 if file doesn't exist
218+
219+
**Example:**
220+
```python
221+
resume_from = DownloadManager.get_resume_position("/sdcard/file.bin")
222+
if resume_from > 0:
223+
headers = {'Range': f'bytes={resume_from}-'}
224+
await DownloadManager.download_url(url, outfile=outfile, headers=headers)
225+
else:
226+
# Start new download
227+
await DownloadManager.download_url(url, outfile=outfile)
228+
```
229+
230+
### Session Management Functions
168231

169232
#### `DownloadManager.is_session_active()`
170233

@@ -254,56 +317,74 @@ async def download_mpk(self, app):
254317
### Resume Partial Download
255318

256319
```python
257-
import os
258-
259320
async def resume_download(self, url, outfile):
260321
"""Resume a partial download using Range headers"""
261-
bytes_written = 0
262-
263-
# Check if partial file exists
264-
try:
265-
bytes_written = os.stat(outfile)[6] # File size
322+
# Use DownloadManager utility to get resume position
323+
bytes_written = DownloadManager.get_resume_position(outfile)
324+
325+
if bytes_written > 0:
266326
print(f"Resuming from {bytes_written} bytes")
267-
except OSError:
327+
headers = {'Range': f'bytes={bytes_written}-'}
328+
else:
268329
print("Starting new download")
330+
headers = None
269331

270332
# Download remaining bytes
271333
success = await DownloadManager.download_url(
272334
url,
273335
outfile=outfile,
274-
headers={'Range': f'bytes={bytes_written}-'}
336+
headers=headers
275337
)
276338

277339
return success
278340
```
279341

280342
**Note:** Server must support HTTP Range requests.
281343

282-
### Error Handling
344+
### Error Handling with Network Detection
283345

284346
```python
285-
async def robust_download(self, url):
286-
"""Download with comprehensive error handling"""
287-
try:
288-
data = await DownloadManager.download_url(url)
289-
290-
if data is None:
291-
print("Download failed (network error or HTTP error)")
292-
return None
293-
294-
if len(data) == 0:
295-
print("Warning: Downloaded empty file")
296-
297-
return data
298-
299-
except ValueError as e:
300-
print(f"Invalid parameters: {e}")
301-
return None
302-
except Exception as e:
303-
print(f"Unexpected error: {e}")
304-
return None
347+
async def robust_download(self, url, outfile):
348+
"""Download with comprehensive error handling and retry"""
349+
max_retries = 3
350+
retry_delay = 2
351+
352+
for attempt in range(max_retries):
353+
try:
354+
success = await DownloadManager.download_url(url, outfile=outfile)
355+
356+
if success:
357+
return True
358+
else:
359+
print("Download failed")
360+
return False
361+
362+
except Exception as e:
363+
if DownloadManager.is_network_error(e):
364+
# Network error - retry with resume
365+
print(f"Network error (attempt {attempt + 1}/{max_retries}): {e}")
366+
367+
if attempt < max_retries - 1:
368+
await TaskManager.sleep(retry_delay)
369+
# Resume from last position
370+
continue
371+
else:
372+
print("Max retries reached")
373+
return False
374+
else:
375+
# Non-network error - don't retry
376+
print(f"Fatal error: {e}")
377+
return False
378+
379+
return False
305380
```
306381

382+
**Benefits:**
383+
- Automatic retry on network errors
384+
- Resume from last position
385+
- No retry on fatal errors (404, invalid URL, etc.)
386+
- User-friendly error messages
387+
307388
## Session Management
308389

309390
### Automatic Lifecycle

0 commit comments

Comments
 (0)