From 2203874ca5f816c8f84b8f9a8213ec7b2c17f6d0 Mon Sep 17 00:00:00 2001 From: BarsTiger Date: Fri, 6 May 2022 11:54:59 +0300 Subject: [PATCH] 1.0.0 --- .gitignore | 1 + LICENSE.txt | 11 +++++++++ README.md | 29 ++++++++++++++++++++++ __init__.py | 3 +++ download.py | 70 +++++++++++++++++++++++++++++++++++++++++++++++++++++ publish.bat | 2 ++ setup.py | 19 +++++++++++++++ 7 files changed, 135 insertions(+) create mode 100644 .gitignore create mode 100644 LICENSE.txt create mode 100644 README.md create mode 100644 __init__.py create mode 100644 download.py create mode 100644 publish.bat create mode 100644 setup.py diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..57f1cb2 --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +/.idea/ \ No newline at end of file diff --git a/LICENSE.txt b/LICENSE.txt new file mode 100644 index 0000000..5f04058 --- /dev/null +++ b/LICENSE.txt @@ -0,0 +1,11 @@ +Copyright 2022 BarsTiger + +Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), +to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, +and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. \ No newline at end of file diff --git a/README.md b/README.md new file mode 100644 index 0000000..60b0a57 --- /dev/null +++ b/README.md @@ -0,0 +1,29 @@ +# ezzdl +Multithreaded downloader with textui colored progress bar + +```bash +pip install ezzdl +``` + +# Use +```python +>>> from ezzdl import dl +>>> dl(["https://github.com/Cactus-0/cabanchik/raw/master/dist/cabanchik.zip", +... "https://github.com/BarsTiger/daun/raw/master/dist/daun.exe", +... "https://github.com/BarsTiger/localhost-mc-open/raw/master/localhost_3000_Mc_fusion.exe"], +... "folder") +[16:20:15] Requesting https://github.com/Cactus-0/cabanchik/raw/master/dist/cabanchik.zip +[16:20:15] Requesting https://github.com/BarsTiger/daun/raw/master/dist/daun.exe +[16:20:15] Requesting https://github.com/BarsTiger/localhost-mc-open/raw/master/localhost_3000_Mc_fusion.exe +[16:20:20] Downloaded D:\Path\To\Folder\folder\daun.exe +[16:20:21] Downloaded D:\Path\To\Folder\folder\cabanchik.zip +[16:20:32] Downloaded D:\Path\To\Folder\folder\localhost_3000_Mc_fusion.exe + cabanchik.zip ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 100.0% • 13.2/13.2 MB • 2.2 MB/s • 0:00:00 + daun.exe ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 100.0% • 11.4/11.4 MB • 2.2 MB/s • 0:00:00 +localhost_3000_Mc_fusion.exe ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 100.0% • 37.7/37.7 MB • 2.3 MB/s • 0:00:00 +``` + +# Features +- Automatically downloads files from a list of urls and generates its names +- Will create a folder with if it doesn't exist +- Colored diff --git a/__init__.py b/__init__.py new file mode 100644 index 0000000..7c189ef --- /dev/null +++ b/__init__.py @@ -0,0 +1,3 @@ +# Cool download manager based on rich and its sample code + +from .download import * diff --git a/download.py b/download.py new file mode 100644 index 0000000..6c2ae63 --- /dev/null +++ b/download.py @@ -0,0 +1,70 @@ +# Idea from rich examples +# https://github.com/Textualize/rich/blob/master/examples/downloader.py + +import os.path +from concurrent.futures import ThreadPoolExecutor +import signal +from functools import partial +from threading import Event +from urllib.request import urlopen +from urllib.parse import unquote +from time import strftime + +from rich.progress import ( + BarColumn, + DownloadColumn, + Progress, + TaskID, + TextColumn, + TimeRemainingColumn, + TransferSpeedColumn, +) +from rich import print + +progress = Progress( + TextColumn("[bold blue]{task.fields[filename]}", justify="right"), + BarColumn(bar_width=None), + "[progress.percentage]{task.percentage:>3.1f}%", + "•", + DownloadColumn(), + "•", + TransferSpeedColumn(), + "•", + TimeRemainingColumn(), +) + + +done_event = Event() + + +def handle_sigint(signum, frame): + done_event.set() + + +signal.signal(signal.SIGINT, handle_sigint) + + +def copy_url(task_id: TaskID, url: str, path: str) -> None: + print(f"[#1d4b6e][{strftime('%H:%M:%S')}][/] Requesting {url}") + response = urlopen(url) + progress.update(task_id, total=int(response.info()["Content-length"])) + with open(path, "wb") as dest_file: + progress.start_task(task_id) + for data in iter(partial(response.read, 32768), b""): + dest_file.write(data) + progress.update(task_id, advance=len(data)) + if done_event.is_set(): + return + print(f"[#1d4b6e][{strftime('%H:%M:%S')}][/] [#8dc789]Downloaded {os.path.abspath(path)}[/]") + + +def dl(urls, dest_dir: str): + if not os.path.isdir(dest_dir): + os.makedirs(dest_dir, exist_ok=True) + with progress: + with ThreadPoolExecutor(max_workers=len(urls)) as pool: + for url in urls: + filename = unquote(url.split("/")[-1]) + dest_path = os.path.join(dest_dir, filename) + task_id = progress.add_task("download", filename=filename, start=False) + pool.submit(copy_url, task_id, url, dest_path) diff --git a/publish.bat b/publish.bat new file mode 100644 index 0000000..240c3ce --- /dev/null +++ b/publish.bat @@ -0,0 +1,2 @@ +python setup.py sdist +python -m twine upload dist/* \ No newline at end of file diff --git a/setup.py b/setup.py new file mode 100644 index 0000000..df2709b --- /dev/null +++ b/setup.py @@ -0,0 +1,19 @@ +from setuptools import setup + +with open("README.md", "r") as fh: + long_description = fh.read() + +setup( + name="ezzdl", + version="1.0.0", + scripts=["download.py"], + author="BarsTiger", + description="Cool download manager based on rich and its sample code", + long_description=long_description, + py_modules=["ezzdl"], + license='MIT', + url='https://github.com/BarsTiger/ezzdl', + long_description_content_type="text/markdown", + keywords=["threading", "thread", "decorator", "crossplatform"], + install_requires=["rich"] +)