Creating a YouTube Video Downloader (Part 3)

Hello everyone!

radowan-nakif-rehan-cYyqhdbJ9TI-unsplash.jpg

Hello everyone and here is your daily unscheduled dose of thumbnail pictures. Photo by Radowan Nakif Rehan on Unsplash

In today's post, I'm going to continue from the last part I stopped at in the development of a YouTube video downloader.

In the previous post, I added the feature of being able to select different qualities when downloading a video, and also did some error handling (You can read about it in this post).

Today, I am going to be modifying the program to allow the user to save the downloaded video in the selected location, rather than saving the downloaded video in the same directory as the program. I also decided to add the feature that will allow the user to download either the YouTube video or the audio only.

Time to begin the development process again...


The first thing I decided to do was to work on the download location feature.

As usual, I created the UI layout for the download location first. I used an entry widget which would hold the download location for the video.


image.png

At first, I was thinking of leaving it at that and entered the download location manually anytime I wanted to download a video. But let's be real here, no one is going to enjoy typing out an entire file directory every time someone needs to download a YouTube video.

That's when I thought about making the 'browse' button for the location entry bar.

I had to make use of the GOAT in my previous post once again which is the Tkinter frame.


image.png

To be honest, I thought that you'll have to create the entire pop-up menu that shows when you want to select the file directory, but I was glad that was not the case. Luckily, the Tkinter module already has functions for those pop-up menus that require you to select a file or a file directory.

What I did next was to create a function that opens up the select file directory menu and assign it to the browse button. When the download location has been selected, the directory is stored in the entry bar.

def select_directory():
    select_dir = filedialog.askdirectory()
    selected_directory.insert(tk.END, select_dir)

This is the function that the 'Browse' button executes.


bandicam 2022-10-24 13-26-22-253.gif

Now that the download location can be gotten. It's time to pass it into the download function.

What I did for this was to get the download location from the entry bar and store it in a path variable as a string. Then this path variable is used as the output path in the line of code that begins the download process.

...
path=str(selected_directory.get())
if not path:
    showerror(title='Error', message='Select a download location')
else:
    video.download(filename=''.join(new_name).replace(' ', '_') + ' ' + '(' + str(selected_quality.get()) + ')' + '.mp4', output_path=path)
...

The next thing is to set the option of downloading either a video or an audio.

For this, I used the frame again (Absolute legend) and assigned a couple of radio buttons to it.


image.png

However, I wanted this set of radio buttons to be different. I wanted the radio buttons for the video resolutions to be deactivated when it is on 'Audio' since you can't download audio in 1080p for instance, and I wanted the radio buttons to be reactivated when 'Video' is selected.

What I did for these buttons was to create a function that deactivates the video qualities if it is on 'Audio' and reactivates them if it is on 'Video'. After creating the function, I assigned this function as a command to the pair of radio buttons.

def disable_buttons():
    if str(down_type.get()) == "Audio":
        for key in side_by_side_widgets:
            side_by_side_widgets[key]['state'] = 'disabled'
    else:
        for key in side_by_side_widgets:
            side_by_side_widgets[key]['state'] = 'enabled'


bandicam 2022-10-24 13-42-09-609.gif

In order to make the program able to download audio, I used the good old 'if-else' statement. If the selected format was 'Audio', the program will locate the audio stream of the video and download it; and if the selected format is 'Video', the program will locate the video stream of the video in the selected quality and download it.

def download():
    try:
        url = YouTube(str(link.get()))
        if str(down_type.get()) == 'Audio':
            audio = url.streams.get_audio_only('mp4')
            name = f'{audio.title}'
            new_name = []
            for x in name:
                if x not in list(string.punctuation):
                    new_name.append(x)
            path=str(selected_directory.get())
            if not path:
                showerror(title='Error', message='Select a download location')
            else:
                audio.download(filename=''.join(new_name).replace(' ', '_') + ' ' + '(' + str(down_type.get()) + ')' + '.mp3', output_path=path)
                showinfo(title='Success', message='Download Completed')

        elif not str(down_type.get()):
            showinfo(title='Error', message='Please select a download format')

        else:
            if not str(selected_quality.get()):
                showinfo(title='Error', message='Please select a video quality')
            else:
                try:
                    video = url.streams.filter(file_extension='mp4').get_by_resolution(str(selected_quality.get()))
                    name=f'{video.title}'
                except AttributeError:
                    showerror(title='Error', message='Selected resolution is unavailable. Try a different resolution.')
                new_name = []
                for x in name:
                    if x not in list(string.punctuation):
                        new_name.append(x)
                path=str(selected_directory.get())
                if not path:
                    showerror(title='Error', message='Select a download location')
                else:
                    video.download(filename=''.join(new_name).replace(' ', '_') + ' ' + '(' + str(selected_quality.get()) + ')' + '.mp4', output_path=path)
                showinfo(title='Success', message='Download Completed')
    except pytube.exceptions.VideoUnavailable:
        showerror(title='Error', message='Link is invalid or the video is unavailable')
    except pytube.exceptions.RegexMatchError:
        showerror(title='Error', message='Please enter a valid YouTube link')

At this point, I realized that I had done everything that I needed to do. But then I thought that it would probably be nice if the user knew if the video was even downloading because nothing happens at first, then Windows thinks that the program has stopped responding and a pop up suddenly appears after a while. So, I thought of adding a progress bar to the program.

There are two different modes for progress bars - determinate and indeterminate modes. In the determinate mode, the progress bar moves in relation to the file size downloaded from the total file size. In short, the kind of progress bar you can find on Steam when you are downloading a game (Idk why I had to use this as an example).


bandicam 2022-10-24 13-57-37-934.gif

While in the indeterminate mode, the progress bar just displays a looping animation. This one is preferably used when you are unsure of the total size of a file.


bandicam 2022-10-24 13-55-26-379.gif

I figured that I would use the determinate mode for the progress bar.

But...

I never thought that making a working progress bar would be such a headache.

# Function for the progress bar
# P.S.: I don't know how to do this yet
def progress(stream, chunk, bytes_remaining):
    max_value = stream.filesize
    bytes_downloaded = max_value - bytes_remaining
    pb['value'] = bytes_downloaded
    pb['maximum'] = max_value
    if bytes_downloaded < max_value:
        pb.after(100, progress())

It took me almost an hour to come up with this, and it still doesn't work. I might come back later to fix this.

After a while, I realized that I couldn't make the progress bar to work with the program at all. So, I decided to get back to it later.


Well, that's all on what I've been working on. If you want to check the program out, you can find the code on my GitHub repo here.

I also made an executable version that you can run without using an IDE. You can find it here.

(If you're trying to download a video with the program, just wait for it. It may look like the program is not responding but it really is. I'll fix that issue later)

Well, that's all for now and thanks for reading :)

H2
H3
H4
3 columns
2 columns
1 column
7 Comments
Ecency