Implement stream-based download_file tool for downloading media files directly to flash or SD card
This commit is contained in:
@@ -0,0 +1,68 @@
|
|||||||
|
import os
|
||||||
|
import urequests
|
||||||
|
from sd_util import SDCardManager
|
||||||
|
|
||||||
|
def download_file(url, dest_filename, use_sd=True):
|
||||||
|
"""Downloads a file from a URL over the network in memory-efficient chunks.
|
||||||
|
|
||||||
|
If use_sd is True, it automatically mounts the microSD card and saves to /sd/dest_filename.
|
||||||
|
Otherwise, it saves to the local flash storage.
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
str: The full path to the downloaded file on success, None on failure.
|
||||||
|
"""
|
||||||
|
dest_path = dest_filename
|
||||||
|
sd_manager = None
|
||||||
|
|
||||||
|
if use_sd:
|
||||||
|
sd_manager = SDCardManager(mount_point='/sd')
|
||||||
|
if not sd_manager.mount():
|
||||||
|
print("Download Error: Could not mount SD card.")
|
||||||
|
return None
|
||||||
|
dest_path = f"/sd/{dest_filename}"
|
||||||
|
|
||||||
|
print(f"Starting download from: {url} -> {dest_path}")
|
||||||
|
|
||||||
|
try:
|
||||||
|
res = urequests.get(url, stream=True)
|
||||||
|
except Exception as e:
|
||||||
|
print(f"HTTP Connection failed: {e}")
|
||||||
|
return None
|
||||||
|
|
||||||
|
if res.status_code != 200:
|
||||||
|
print(f"HTTP Error: Received status code {res.status_code}")
|
||||||
|
res.close()
|
||||||
|
return None
|
||||||
|
|
||||||
|
try:
|
||||||
|
# Read from socket stream in chunks to avoid OutOfMemory errors
|
||||||
|
chunk_size = 4096
|
||||||
|
total_downloaded = 0
|
||||||
|
|
||||||
|
with open(dest_path, 'wb') as f:
|
||||||
|
while True:
|
||||||
|
# Read chunk from the raw socket connection stream
|
||||||
|
chunk = res.raw.read(chunk_size)
|
||||||
|
if not chunk:
|
||||||
|
break
|
||||||
|
f.write(chunk)
|
||||||
|
total_downloaded += len(chunk)
|
||||||
|
|
||||||
|
# Dynamic logging
|
||||||
|
if total_downloaded % (chunk_size * 25) == 0:
|
||||||
|
print(f"Downloaded {total_downloaded // 1024} KB...")
|
||||||
|
|
||||||
|
print(f"Download complete! Saved {total_downloaded} bytes to '{dest_path}'.")
|
||||||
|
return dest_path
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
print(f"Error writing to file: {e}")
|
||||||
|
# Clean up partial file on failure
|
||||||
|
try:
|
||||||
|
os.remove(dest_path)
|
||||||
|
except:
|
||||||
|
pass
|
||||||
|
return None
|
||||||
|
|
||||||
|
finally:
|
||||||
|
res.close()
|
||||||
@@ -263,6 +263,19 @@ class MCPServer:
|
|||||||
},
|
},
|
||||||
"required": ["wav_base64"]
|
"required": ["wav_base64"]
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "download_file",
|
||||||
|
"description": "Download a file from a URL over Wi-Fi directly to the board storage or microSD card.",
|
||||||
|
"inputSchema": {
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"url": {"type": "string", "description": "The URL of the file to download"},
|
||||||
|
"filename": {"type": "string", "description": "The destination filename (e.g. 'podcast1.wav')"},
|
||||||
|
"use_sd": {"type": "boolean", "description": "Save to microSD card if true, or local flash if false (default true)", "default": True}
|
||||||
|
},
|
||||||
|
"required": ["url", "filename"]
|
||||||
|
}
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
@@ -522,5 +535,17 @@ class MCPServer:
|
|||||||
else:
|
else:
|
||||||
raise RuntimeError("Failed to play decoded audio stream. Check format (16kHz 16-bit PCM WAV recommended).")
|
raise RuntimeError("Failed to play decoded audio stream. Check format (16kHz 16-bit PCM WAV recommended).")
|
||||||
|
|
||||||
|
elif name == "download_file":
|
||||||
|
url = str(args.get("url"))
|
||||||
|
filename = str(args.get("filename"))
|
||||||
|
use_sd = bool(args.get("use_sd", True))
|
||||||
|
|
||||||
|
from download_util import download_file
|
||||||
|
saved_path = download_file(url, filename, use_sd=use_sd)
|
||||||
|
if saved_path:
|
||||||
|
return f"Successfully downloaded file to '{saved_path}'."
|
||||||
|
else:
|
||||||
|
raise RuntimeError(f"Failed to download file from '{url}'.")
|
||||||
|
|
||||||
else:
|
else:
|
||||||
raise ValueError(f"Unknown tool: {name}")
|
raise ValueError(f"Unknown tool: {name}")
|
||||||
|
|||||||
Reference in New Issue
Block a user