Skip to content

Commit 5dd2409

Browse files
Move download_url() to DownloadManager
1 parent 7cdea5f commit 5dd2409

File tree

5 files changed

+779
-103
lines changed

5 files changed

+779
-103
lines changed

internal_filesystem/builtin/apps/com.micropythonos.appstore/assets/appstore.py

Lines changed: 5 additions & 101 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,3 @@
1-
import aiohttp
21
import lvgl as lv
32
import json
43
import requests
@@ -7,7 +6,7 @@
76

87
from mpos.apps import Activity, Intent
98
from mpos.app import App
10-
from mpos import TaskManager
9+
from mpos import TaskManager, DownloadManager
1110
import mpos.ui
1211
from mpos.content.package_manager import PackageManager
1312

@@ -28,7 +27,6 @@ class AppStore(Activity):
2827
app_index_url_badgehub = _BADGEHUB_API_BASE_URL + "/" + _BADGEHUB_LIST
2928
app_detail_url_badgehub = _BADGEHUB_API_BASE_URL + "/" + _BADGEHUB_DETAILS
3029
can_check_network = True
31-
aiohttp_session = None # one session for the whole app is more performant
3230

3331
# Widgets:
3432
main_screen = None
@@ -39,7 +37,6 @@ class AppStore(Activity):
3937
progress_bar = None
4038

4139
def onCreate(self):
42-
self.aiohttp_session = aiohttp.ClientSession()
4340
self.main_screen = lv.obj()
4441
self.please_wait_label = lv.label(self.main_screen)
4542
self.please_wait_label.set_text("Downloading app index...")
@@ -62,11 +59,8 @@ def onResume(self, screen):
6259
else:
6360
TaskManager.create_task(self.download_app_index(self.app_index_url_github))
6461

65-
def onDestroy(self, screen):
66-
await self.aiohttp_session.close()
67-
6862
async def download_app_index(self, json_url):
69-
response = await self.download_url(json_url)
63+
response = await DownloadManager.download_url(json_url)
7064
if not response:
7165
self.please_wait_label.set_text(f"Could not download app index from\n{json_url}")
7266
return
@@ -152,7 +146,7 @@ async def download_icons(self):
152146
break
153147
if not app.icon_data:
154148
try:
155-
app.icon_data = await TaskManager.wait_for(self.download_url(app.icon_url), 5) # max 5 seconds per icon
149+
app.icon_data = await TaskManager.wait_for(DownloadManager.download_url(app.icon_url), 5) # max 5 seconds per icon
156150
except Exception as e:
157151
print(f"Download of {app.icon_url} got exception: {e}")
158152
continue
@@ -177,96 +171,6 @@ def show_app_detail(self, app):
177171
intent.putExtra("appstore", self)
178172
self.startActivity(intent)
179173

180-
'''
181-
This async download function can be used in 3 ways:
182-
- with just a url => returns the content
183-
- with a url and an outfile => writes the content to the outfile
184-
- with a url and a chunk_callback => calls the chunk_callback(chunk_data) for each chunk
185-
186-
Optionally:
187-
- progress_callback is called with the % (0-100) progress
188-
- if total_size is not provided, it will be taken from the response headers (if present) or default to 100KB
189-
- a dict of headers can be passed, for example: headers['Range'] = f'bytes={self.bytes_written_so_far}-'
190-
191-
Can return either:
192-
- the actual content
193-
- None: if the content failed to download
194-
- True: if the URL was successfully downloaded (and written to outfile, if provided)
195-
- False: if the URL was not successfully download and written to outfile
196-
'''
197-
async def download_url(self, url, outfile=None, total_size=None, progress_callback=None, chunk_callback=None, headers=None):
198-
print(f"Downloading {url}")
199-
#await TaskManager.sleep(4) # test slowness
200-
try:
201-
async with self.aiohttp_session.get(url, headers=headers) as response:
202-
if response.status < 200 or response.status >= 400:
203-
return False if outfile else None
204-
205-
# Figure out total size
206-
print("headers:") ; print(response.headers)
207-
if total_size is None:
208-
total_size = response.headers.get('Content-Length') # some servers don't send this in the headers
209-
if total_size is None:
210-
print("WARNING: Unable to determine total_size from server's reply and function arguments, assuming 100KB")
211-
total_size = 100 * 1024
212-
213-
fd = None
214-
if outfile:
215-
fd = open(outfile, 'wb')
216-
if not fd:
217-
print("WARNING: could not open {outfile} for writing!")
218-
return False
219-
chunks = []
220-
partial_size = 0
221-
chunk_size = 1024
222-
223-
print(f"download_url {'writing to ' + outfile if outfile else 'downloading'} {total_size} bytes in chunks of size {chunk_size}")
224-
225-
while True:
226-
tries_left = 3
227-
chunk_data = None
228-
while tries_left > 0:
229-
try:
230-
chunk_data = await TaskManager.wait_for(response.content.read(chunk_size), 10)
231-
break
232-
except Exception as e:
233-
print(f"Waiting for response.content.read of next chunk_data got error: {e}")
234-
tries_left -= 1
235-
236-
if tries_left == 0:
237-
print("ERROR: failed to download chunk_data, even with retries!")
238-
if fd:
239-
fd.close()
240-
return False if outfile else None
241-
242-
if chunk_data:
243-
# Output
244-
if fd:
245-
fd.write(chunk_data)
246-
elif chunk_callback:
247-
await chunk_callback(chunk_data)
248-
else:
249-
chunks.append(chunk_data)
250-
# Report progress
251-
partial_size += len(chunk_data)
252-
progress_pct = round((partial_size * 100) / int(total_size))
253-
print(f"progress: {partial_size} / {total_size} bytes = {progress_pct}%")
254-
if progress_callback:
255-
await progress_callback(progress_pct)
256-
#await TaskManager.sleep(1) # test slowness
257-
else:
258-
print("chunk_data is None while there was no error so this was the last one.\n Finished downloading {url}")
259-
if fd:
260-
fd.close()
261-
return True
262-
elif chunk_callback:
263-
return True
264-
else:
265-
return b''.join(chunks)
266-
except Exception as e:
267-
print(f"download_url got exception {e}")
268-
return False if outfile else None
269-
270174
@staticmethod
271175
def badgehub_app_to_mpos_app(bhapp):
272176
#print(f"Converting {bhapp} to MPOS app object...")
@@ -293,7 +197,7 @@ def badgehub_app_to_mpos_app(bhapp):
293197

294198
async def fetch_badgehub_app_details(self, app_obj):
295199
details_url = self.app_detail_url_badgehub + "/" + app_obj.fullname
296-
response = await self.download_url(details_url)
200+
response = await DownloadManager.download_url(details_url)
297201
if not response:
298202
print(f"Could not download app details from from\n{details_url}")
299203
return
@@ -578,7 +482,7 @@ async def download_and_install(self, app_obj, dest_folder):
578482
pass
579483
temp_zip_path = "tmp/temp.mpk"
580484
print(f"Downloading .mpk file from: {zip_url} to {temp_zip_path}")
581-
result = await self.appstore.download_url(zip_url, outfile=temp_zip_path, total_size=download_url_size, progress_callback=self.pcb)
485+
result = await DownloadManager.download_url(zip_url, outfile=temp_zip_path, total_size=download_url_size, progress_callback=self.pcb)
582486
if result is not True:
583487
print("Download failed...") # Would be good to show an error to the user if this failed...
584488
else:

internal_filesystem/lib/mpos/__init__.py

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
from .app.app import App
33
from .app.activity import Activity
44
from .net.connectivity_manager import ConnectivityManager
5+
from .net import download_manager as DownloadManager
56
from .content.intent import Intent
67
from .activity_navigator import ActivityNavigator
78
from .content.package_manager import PackageManager
@@ -13,7 +14,7 @@
1314
from .app.activities.share import ShareActivity
1415

1516
__all__ = [
16-
"App", "Activity", "ConnectivityManager", "Intent",
17-
"ActivityNavigator", "PackageManager",
17+
"App", "Activity", "ConnectivityManager", "DownloadManager", "Intent",
18+
"ActivityNavigator", "PackageManager", "TaskManager",
1819
"ChooserActivity", "ViewActivity", "ShareActivity"
1920
]
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1 +1,3 @@
11
# mpos.net module - Networking utilities for MicroPythonOS
2+
3+
from . import download_manager

0 commit comments

Comments
 (0)