Skip to content

Commit

Permalink
Create resume download functionality
Browse files Browse the repository at this point in the history
  • Loading branch information
Saltssaumure committed Apr 14, 2024
1 parent 9c466cc commit b8e25d5
Show file tree
Hide file tree
Showing 3 changed files with 63 additions and 46 deletions.
25 changes: 15 additions & 10 deletions download.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,28 +10,33 @@
from lib.lists import make_all_urls, split_list
from lib.pretty import pprint, time
from lib.config import get_config
from lib.download import download_batch
from lib.downloader import download_batch


def start():
"""Run AutoBeatPack"""
pprint("==== AutoBeatPack by Saltssaumure ====\n")

config_filename = "config.ini"
succeed_text = f"\nAll complete - {time()}"
failed_text = f"\nStopped - {time()}"
try:
first, last, batch_size, abs_download_folder = get_config()

first, last, batch_size, abs_download_folder = get_config(config_filename)

all_urls = split_list(make_all_urls(first, last), batch_size)
for idx, batch_urls in enumerate(all_urls):
asyncio.run(download_batch(idx+1, batch_urls, abs_download_folder))

pprint(f"\nAll complete - {time()}")
pprint(succeed_text)
except KeyboardInterrupt:
pprint(f"\nDownload(s) cancelled - {time()}")
except TimeoutError as e:
pprint(f"\nDownload(s) cancelled - {time()}: Connection timed out. {e}")
except aiohttp.client_exceptions.ClientConnectorError as e:
pprint(f"\nDownload(s) cancelled - {time()}: Can't connect. {e}")
except ValueError:
pprint("\nInvalid config.txt value(s), see README for help.")
pprint(f"{failed_text}: User cancelled.")
except TimeoutError as err:
pprint(f"{failed_text}: Connection timed out. {err}")
except aiohttp.client_exceptions.ClientConnectorError as err:
pprint(f"{failed_text}: Can't connect. {err}")
except (FileNotFoundError, ValueError) as err:
pprint(f"{failed_text}: Invalid {config_filename} - {err}")


if __name__ == "__main__":
Expand Down
15 changes: 10 additions & 5 deletions lib/config.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,17 +16,22 @@ def try_user(config, profile):
config[profile] # pylint: disable=pointless-statement
return profile
except KeyError:
profile = input(f"{q(profile)} not found, try again:\t")
profile = input(f"{q(profile)} not found:\t")
return try_user(config, profile)


def get_config():
def get_config(config_filename):
"""Returns config values from config.txt"""
raw_config = configparser.ConfigParser()
raw_config.read("config.ini")
if not path.exists(config_filename):
raise FileNotFoundError("file not found.")
raw_config.read(config_filename)
if len(raw_config.sections()) == 0:
raise ValueError("file missing profiles.")

profile = "DEFAULT"
if input("Use DEFAULT config? y/n\t").lower() != "y":
profile = input("Config profile name:\t")
if input(f"Use {q(profile)} profile? [Y/n]\t").lower() == "n":
profile = input("Config profile name:\t\t")
profile = try_user(raw_config, profile)
config_user = raw_config[profile]

Expand Down
69 changes: 38 additions & 31 deletions lib/download.py → lib/downloader.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,14 +13,20 @@
__all__ = ["download_batch", "download_decision", "download_file"]


async def download_file(abs_filename, response, overwrite):
async def download_file(abs_filename, response, expected_size, mode_desc, filesize=0):
"""Download file and report progress"""
expected_size = int(response.headers["Content-Length"])
mode = "wb" if overwrite else "xb"
modes = {
"write": "xb",
"overwrite": "wb",
"append": "ab",
}
if mode_desc in modes:
mode = modes[mode_desc]
else:
raise ValueError(f"invalid mode {q(mode_desc)}")
filename = os.path.basename(abs_filename)

with open(abs_filename, mode=mode) as file:
filesize = 0
with open(abs_filename, mode=mode) as file: # pylint: disable=unspecified-encoding
old_prog = 0
# Get and write chunks of 1024 bytes
# Print progress every 1%
Expand All @@ -45,38 +51,39 @@ async def download_decision(url, abs_download_folder):
"""Decide whether to download file from url based on local file contents."""
filename = os.path.basename(parse.unquote(parse.urlparse(url).path))
abs_filename = os.path.join(abs_download_folder, filename)

filesize = os.path.getsize(abs_filename) if os.path.exists(abs_filename) else 0

if not os.path.exists(abs_download_folder):
os.makedirs(abs_download_folder)

# Get expected size of whole file
async with aiohttp.ClientSession() as session:
async with session.get(url) as response:
try:
expected_size = int(response.headers["Content-Length"])

p_starting = ind(f'Starting {q(filename)} (server: {await size(expected_size)})')
p_skipped = ind(f'Skipped {q(filename)}')

filesize = os.path.getsize(abs_filename)

p_redownload = ind(
f'''Redownload {q(filename)}? y/n
(local: {await size(filesize)})
(server: {await size(expected_size)})\t''')

if filesize == expected_size:
pprint(p_skipped + " (match)")
elif filesize == 0:
pprint(p_starting)
await download_file(abs_filename, response, overwrite=True)
else:
if input(p_redownload + "\t").lower() == "y":
pprint(p_starting)
await download_file(abs_filename, response, overwrite=True)
else:
pprint(p_skipped + " (manual)")
except OSError:
expected_size = int(response.headers["Content-Length"])

# Decide whether to download file
async with aiohttp.ClientSession(headers={"Range": f"bytes={filesize}-"}) as session:
async with session.get(url) as response:
p_starting = ind(f"Starting {q(filename)} ({await size(expected_size)})")
p_skipped = ind(f"Skipped {q(filename)} (match)")
p_resuming = ind(
f"Resuming {q(filename)} ({await size(filesize)} of {await size(expected_size)})"
)

if not os.path.exists(abs_filename):
pprint(p_starting)
await download_file(abs_filename, response, expected_size, mode_desc="write")
elif filesize == 0:
pprint(p_starting)
await download_file(abs_filename, response, overwrite=False)
await download_file(abs_filename, response, expected_size, mode_desc="overwrite")
elif filesize >= expected_size:
pprint(p_skipped)
else:
pprint(p_resuming)
await download_file(
abs_filename, response, expected_size, mode_desc="append", filesize=filesize
)


async def download_batch(batch, urls, abs_download_folder):
Expand Down

0 comments on commit b8e25d5

Please sign in to comment.