diff --git a/usr/bin/spoc-app b/usr/bin/spoc-app index 9c2004b..6085771 100644 --- a/usr/bin/spoc-app +++ b/usr/bin/spoc-app @@ -5,7 +5,7 @@ import argparse import os from spoc import publisher -from spoc.utils import readable_size +from spoc.cli import readable_size ACTION_LIST = 1 ACTION_INSTALL = 2 diff --git a/usr/bin/spoc-image b/usr/bin/spoc-image index d46f1bd..16cadf0 100644 --- a/usr/bin/spoc-image +++ b/usr/bin/spoc-image @@ -8,7 +8,7 @@ from spoc import repo_online from spoc import repo_publish from spoc.image import Image from spoc.imagebuilder import ImageBuilder -from spoc.utils import ActionItem, readable_size +from spoc.cli import ActionQueue, ActionItem, readable_size ACTION_LIST = 1 ACTION_DOWNLOAD = 2 @@ -36,15 +36,14 @@ def listing(repo_type): print(image) def download(image_name): - plan = [] + actionqueue = ActionQueue() local_images = repo_local.get_images() for layer in repo_online.get_image(image_name)['layers']: if layer not in local_images: image = Image(layer, False) - plan.append(ActionItem(f'Downloading {image_name}', image.download)) - plan.append(ActionItem(f'Unpacking {image_name}', image.unpack_downloaded)) - for item in plan: - item.run() + actionqueue.append(ActionItem(f'Downloading {image_name}', image.download)) + actionqueue.append(ActionItem(f'Unpacking {image_name}', image.unpack_downloaded)) + actionqueue.process() def delete(image_name): Image(image_name, False).delete() diff --git a/usr/lib/python3.8/spoc/cli.py b/usr/lib/python3.8/spoc/cli.py new file mode 100644 index 0000000..1f36680 --- /dev/null +++ b/usr/lib/python3.8/spoc/cli.py @@ -0,0 +1,49 @@ +# -*- coding: utf-8 -*- + +import time + +from concurrent.futures import ThreadPoolExecutor + +SIZE_PREFIXES = ('', 'k', 'M', 'G', 'T', 'P', 'E', 'Z', 'Y') + +class ActionQueue: + def __init__(self): + self.queue = [] + + def append(self, actionitem): + self.queue.append(actionitem) + + def process(self): + index = 0 + queue_length = len(self.queue) + for item in self.queue: + index += 1 + item.text = f'[{index}/{queue_length}] {item.text}' + item.run() + +class ActionItem: + def __init__(self, text, action): + self.text = text + self.action = action + self.units_total = 0 + self.units_done = 0 + + def run(self): + with ThreadPoolExecutor() as executor: + future = executor.submit(self.action, self) + while not future.done(): + time.sleep(0.2) + self.print_status() + # Get the result of the future and let it raise exception, if there was any + data = future.result() + self.print_status('\n') + + def print_status(self, end='\r'): + print(f'\x1b[K{self.text} ({self.units_done}/{self.units_total}) [{self.units_done/self.units_total*100:.2f} %]', end=end) + +def readable_size(bytes): + i = 0 + while bytes > 1024: + i += 1 + bytes /= 1024 + return f'{bytes:.2f} {SIZE_PREFIXES[i]}B' diff --git a/usr/lib/python3.8/spoc/repo_online.py b/usr/lib/python3.8/spoc/repo_online.py index c531bae..ba8656d 100644 --- a/usr/lib/python3.8/spoc/repo_online.py +++ b/usr/lib/python3.8/spoc/repo_online.py @@ -55,7 +55,7 @@ def download_archive(archive_url, archive_path, expected_hash, observer): # Download archive via http(s) and store in temporary directory with open(archive_path, 'wb') as f, requests.Session() as session: resource = session.get(archive_url, stream=True) - for chunk in resource.iter_content(chunk_size=None): + for chunk in resource.iter_content(chunk_size=64*1024): if chunk: observer.units_done += f.write(chunk) diff --git a/usr/lib/python3.8/spoc/utils.py b/usr/lib/python3.8/spoc/utils.py deleted file mode 100644 index a2099eb..0000000 --- a/usr/lib/python3.8/spoc/utils.py +++ /dev/null @@ -1,21 +0,0 @@ -# -*- coding: utf-8 -*- - -SIZE_PREFIXES = ('', 'k', 'M', 'G', 'T', 'P', 'E', 'Z', 'Y') - -class ActionItem: - def __init__(self, text, action): - self.text = text - self.action = action - self.units_total = 0 - self.units_done = 0 - - def run(self): - print(self.text) - self.action(self) - -def readable_size(bytes): - i = 0 - while bytes > 1024: - i += 1 - bytes /= 1024 - return f'{bytes:.2f} {SIZE_PREFIXES[i]}B'