diff --git a/ytmdl/download.py b/ytmdl/download.py index 3edce29..a2e5863 100644 --- a/ytmdl/download.py +++ b/ytmdl/download.py @@ -2,83 +2,211 @@ import sys import time from os import path -from ytmdl import utility +from os import popen +import argparse +# import traceback ## Required to debug at times. -def download(url, des): - try: - # Download files with a progressbar showing the percentage - u = urllib.request.urlopen(url) - f = open(des, 'wb') - meta = u.info() - file_size = int(meta["Content-Length"]) +def arguments(): + """Parse the arguments.""" + parser = argparse.ArgumentParser() - file_size_dl = 0 - block_sz = 8192 + parser.add_argument('URL', help="URL of the file", + default=None, type=str) + parser.add_argument('des', help="The name of the file\ + to be saved with.", default=None, nargs='?') - beg_time = time.time() - while True: - buffer = u.read(block_sz) - if not buffer: - break + args = parser.parse_args() + return args - file_size_dl += len(buffer) - f.write(buffer) - # Calculate speed - speed = (file_size_dl / 1024) / (time.time() - beg_time) +class Download: - # Calculate time left - time_left = round(((file_size - file_size_dl) / 1024) / speed) - time_unit = 's' + def __init__(self, URL, des=None): + self.URL = URL + self.des = des + self.headers = {} + self.f_size = 0 - # Convert to min or hours as req - if time_left > 3600: - time_left = round(time_left / 3600) - time_unit = 'h' - elif time_left > 60: - time_left = round(time_left / 60) - time_unit = 'm' + def _build_headers(self, rem): + """Build headers according to requirement.""" + self.headers = {"Range": "bytes={}-".format(rem)} + print("Trying to resume download at: {} bytes".format(rem)) - # Calculate percentage - percent = file_size_dl * 100. / file_size + def _preprocess_conn(self): + """Make necessary things for the connection.""" + self.req = urllib.request.Request(url=self.URL, headers=self.headers) - # file_size to show - if file_size_dl > (1024 * 1024): - file_size_to_disp = file_size_dl / (1024 * 1024) - dw_unit = "MB's" - elif file_size_dl > 1024: - file_size_to_disp = file_size_dl / 1024 - dw_unit = "kb's" + try: + self.conn = urllib.request.urlopen(self.req) + except Exception as e: + print("ERROR: {}".format(e)) + exit() - # Basename - basename = path.basename(des) + self.f_size = int(self.conn.info()['Content-Length']) - # Calculate amount of space req in between - length = utility.get_terminal_length() + def _get_terminal_length(self): + """Return the length of the terminal.""" + rows, cols = popen('stty size', 'r').read().split() + return int(cols) - stuff_len = len(basename) + 13 + 17 + 7 + 26 + 5 - space = 0 + def _parse_destination(self): + # Check if the des is passed + if self.des is not None: + if path.isdir(self.des): + self.des = path.join(self.des, self._get_name()) + else: + self.des = self._get_name() - if stuff_len < length: - space = length - stuff_len - elif stuff_len > length: - basename = basename[:(length - stuff_len) - 2] + '..' + # Put a check to see if file already exists. + # Try to resume it if that's true + if path.exists(self.des): + rem_size = path.getsize(self.des) + self._build_headers(rem_size) - status = r"%s %s %0.2f %s |%d kbps| ETA: %s %s |%-20s| %3.2f%%" % (basename, space * " ", file_size_to_disp, dw_unit, speed, time_left, time_unit, "-" * int(percent / 5), percent) - sys.stdout.write('\r') - sys.stdout.write(status) - sys.stdout.flush() + def _get_name(self): + """Try to get the name of the file from the URL.""" + + name = 'temp' + temp_url = self.URL - f.close() + split_url = temp_url.split('/') - print() - return True - except ConnectionError: - print("Connection Error!") - return False + for name in split_url[::-1]: + if name != '': + break + + return name + + def _format_size(self, size): + """Format the passed size. + + If its more than an 1 Mb then return the size in Mb's + else return it in Kb's along with the unit. + """ + formatted_size = size + dw_unit = 'bytes' + + if formatted_size > (1024 * 1024 * 1024): + formatted_size = size / (1024 * 1024 * 1024) + dw_unit = "GB's" + elif formatted_size > (1024 * 1024): + formatted_size = size / (1024 * 1024) + dw_unit = "MB's" + elif formatted_size > 1024: + formatted_size = size / 1024 + dw_unit = "kb's" + + return (formatted_size, dw_unit) + + def _format_time(self, time_left): + """Format the passed time depending.""" + + if time_left > 3600: + time_left = round(time_left / 3600) + time_unit = 'h' + elif time_left > 60: + time_left = round(time_left / 60) + time_unit = 'm' + + return time_left, time_unit + + def _get_speed_n_time(self, file_size_dl, beg_time, cur_time): + """Return the speed and time depending on the passed arguments.""" + + # Calculate speed + speed = (file_size_dl / 1024) / (cur_time - beg_time) + + # Calculate time left + time_left = round(((self.f_size - file_size_dl) / 1024) / speed) + time_unit = 's' + + # Convert to min or hours as req + if time_left > 3600: + time_left = round(time_left / 3600) + time_unit = 'h' + elif time_left > 60: + time_left = round(time_left / 60) + time_unit = 'm' + + return speed, time_left, time_unit + + def download(self): + try: + self._parse_destination() + + # Download files with a progressbar showing the percentage + self._preprocess_conn() + WSTREAM = open(self.des, 'wb') + + formatted_file_size, dw_unit = self._format_size(self.f_size) + print("Size: {} {}".format(round(formatted_file_size), dw_unit)) + print("Saving as: {}".format(self.des)) + + file_size_dl = 0 + block_sz = 8192 + + beg_time = time.time() + while True: + buffer = self.conn.read(block_sz) + if not buffer: + break + + file_size_dl += len(buffer) + WSTREAM.write(buffer) + + # Initialize all the variables that cannot be calculated + # to '' + speed = '' + time_left = '' + time_unit = '' + percent = '' + + if self.f_size is not None: + speed, time_left, time_unit = self._get_speed_n_time( + file_size_dl, + beg_time, + cur_time=time.time() + ) + percent = file_size_dl * 100 / self.f_size + + # Get basename + self.basename = path.basename(self.des) + + # Calculate amount of space req in between + length = self._get_terminal_length() + + stuff_len = len(self.basename) + 13 + 17 + 7 + 26 + 3 + space = 0 + + if stuff_len < length: + space = length - stuff_len + elif stuff_len > length: + self.basename = self.basename[:(length - stuff_len) - 2] + '..' + + f_size_disp, dw_unit = self._format_size(file_size_dl) + if self.f_size is not None: + status = r"%s %s %0.2f %s |%d kbps| ETA: %s %s |%-20s| |%3.2f%%|" % (self.basename, space * " ", f_size_disp, dw_unit, speed, time_left, time_unit, "-" * int(percent / 5), percent) + else: + status = r"%s %s %0.2f %s" %(self.basename, space * " ", f_size_disp, dw_unit) + sys.stdout.write('\r') + sys.stdout.write(status) + sys.stdout.flush() + + WSTREAM.close() + + print() + return True + except KeyboardInterrupt: + sys.stdout.flush() + print("Keyboard Interrupt passed. Exitting peacefully.") + exit() + except Exception as e: + print("ERROR: {}".format(e)) + return False if __name__ == "__main__": - download("http://speedtest.ftp.otenet.gr/files/test100k.db", 'nana.mkv') + args = arguments() + Download(args.URL, args.des).download() diff --git a/ytmdl/yt.py b/ytmdl/yt.py index abe3464..b68191a 100644 --- a/ytmdl/yt.py +++ b/ytmdl/yt.py @@ -59,19 +59,25 @@ def dw(value, song_name='ytmdl_temp.mp3'): # Replace the spaces with hashes song_name = song_name.replace(' ', '#') + # The directory where we will download to. + dw_dir = defaults.DEFAULT.SONG_TEMP_DIR + + if not os.path.exists(dw_dir): + os.makedirs(dw_dir) + # Name of the temp file - name = os.path.join(defaults.DEFAULT.SONG_TEMP_DIR, song_name) + name = os.path.join(dw_dir, song_name) # Start downloading the song """response = requests.get(url, stream=True) with open(name, 'wb') as out_file: copyfileobj(response.raw, out_file) """ - download.download(url, name) + download.Download(url, name).download() return name - except Exception: - return False + except Exception as e: + return e def get_href(url):