Code for How to Make a YouTube Video Downloader in Python Tutorial


View on Github

youtube_downloader_cli.py

from pytube import YouTube

# the function takes the video url as an argument
def video_downloader(video_url):
    # passing the url to the YouTube object
    my_video = YouTube(video_url)
    # downloading the video in high resolution
    my_video.streams.get_highest_resolution().download()
    # return the video title
    return my_video.title
# the try statement will run if there are no errors
try:
    # getting the url from the user
    youtube_link = input('Enter the YouTube link:')
    print(f'Downloading your Video, please wait.......')
    # passing the url to the function
    video = video_downloader(youtube_link)
    # printing the video title
    print(f'"{video}" downloaded succussfully!!')
#the except will catch ValueError, URLError, RegexMatchError and simalar
except:
    print(f'Failed to download video\nThe '\
          'following might be the causes\n->No internet '\
          'connection\n->Invalid video link')
    
# YouTube(url).streams.filter(res="360p").first().download()
#YouTube(url).streams.first().download()

youtube_downloader_ui.py

from tkinter import *
from tkinter import ttk
from pytube import YouTube
from tkinter.messagebox import showinfo, showerror, askokcancel
import threading


# the function to download the video
def download_video():
    # the try statement to excute the download the video code
    try:
        # getting video url from entry
        video_link = url_entry.get()
        # getting video resolution from Combobox
        resolution = video_resolution.get()
        # checking if the entry and combobox is empty
        if resolution == '' and video_link == '':
            # display error message when combobox is empty
            showerror(title='Error', message='Please enter both the video URL and resolution!!')
        # checking if the resolution is empty
        elif resolution == '':
            # display error message when combobox is empty
            showerror(title='Error', message='Please select a video resolution!!')
        # checking if the comboxbox value is None  
        elif resolution == 'None':
            # display error message when combobox value is None
            showerror(title='Error', message='None is an invalid video resolution!!\n'\
                    'Please select a valid video resolution')    
        # else let's download the video  
        else:
            # this try statement will run if the resolution exists for the video
            try:   
                # this function will track the video download progress
                def on_progress(stream, chunk, bytes_remaining):
                    # the total size of the video
                    total_size = stream.filesize    
                    # this function will get the size of the video
                    def get_formatted_size(total_size, factor=1024, suffix='B'):
                        # looping through the units
                        for unit in ["", "K", "M", "G", "T", "P", "E", "Z"]:
                            if total_size < factor:
                                return f"{total_size:.2f}{unit}{suffix}"
                            total_size /= factor
                        # returning the formatted video size
                        return f"{total_size:.2f}Y{suffix}"
                    
                    # getting the formatted video size calling the function
                    formatted_size = get_formatted_size(total_size)
                    # the size downloaded after the start
                    bytes_downloaded = total_size - bytes_remaining
                    # the percentage downloaded after the start
                    percentage_completed = round(bytes_downloaded / total_size * 100)
                    # updating the progress bar value
                    progress_bar['value'] = percentage_completed
                    # updating the empty label with the percentage value
                    progress_label.config(text=str(percentage_completed) + '%, File size:' + formatted_size)
                    # updating the main window of the app
                    window.update()
                
                # creating the YouTube object and passing the the on_progress function
                video = YouTube(video_link, on_progress_callback=on_progress)
                # downlaoding the actual video  
                video.streams.filter(res=resolution).first().download()
                # popup for dispalying the video downlaoded success message
                showinfo(title='Download Complete', message='Video has been downloaded successfully.')
                
                # ressetting the progress bar and the progress label
                progress_label.config(text='')
                progress_bar['value'] = 0
            # the except will run when the resolution is not available or invalid
            except:
                showerror(title='Download Error', message='Failed to download video for this resolution')
                # ressetting the progress bar and the progress label
                progress_label.config(text='')
                progress_bar['value'] = 0
    # the except statement to catch errors, URLConnectError, RegMatchError  
    except:
        # popup for displaying the error message
        showerror(title='Download Error', message='An error occurred while trying to ' \
                    'download the video\nThe following could ' \
                    'be the causes:\n->Invalid link\n->No internet connection\n'\
                     'Make sure you have stable internet connection and the video link is valid')
        # ressetting the progress bar and the progress label
        progress_label.config(text='')
        progress_bar['value'] = 0
        

# function for searching video resolutions
def searchResolution():
    # getting video url from entry
    video_link = url_entry.get()
    # checking if the video link is empty
    if video_link == '':
        showerror(title='Error', message='Provide the video link please!')
    # if video link not empty search resolution  
    else:
        try:
            # creating a YouTube object
            video = YouTube(video_link)
            # an empty list that will hold all the video resolutions
            resolutions = []
            # looping through the video streams
            for i in video.streams.filter(file_extension='mp4'):
                # adding the video resolutions to the resolutions list
                resolutions.append(i.resolution)
            # adding the resolutions to the combobox
            video_resolution['values'] = resolutions
            # when search is complete notify the user
            showinfo(title='Search Complete', message='Check the Combobox for the available video resolutions')
        # catch any errors if they occur  
        except:
            # notify the user if errors are caught
            showerror(title='Error', message='An error occurred while searching for video resolutions!\n'\
                'Below might be the causes\n->Unstable internet connection\n->Invalid link')


# the function to run the searchResolution function as a thread
def searchThread():
    t1 = threading.Thread(target=searchResolution)
    t1.start()
    
    
# the function to run the download_video function as a thread   
def downloadThread():
    t2 = threading.Thread(target=download_video)
    t2.start()


# creates the window using Tk() fucntion
window = Tk()

# creates title for the window
window.title('YouTube Video Downloader')
# dimensions and position of the window
window.geometry('500x460+430+180')
# makes the window non-resizable
window.resizable(height=FALSE, width=FALSE)
# creates the canvas for containing all the widgets
canvas = Canvas(window, width=500, height=400)
canvas.pack()
# loading the logo
logo = PhotoImage(file='youtubelogo.png')
# creates dimensions of the logo
logo = logo.subsample(10, 10)
# adding the logo to the canvas
canvas.create_image(250, 80, image=logo)
"""Styles for the widgets"""
# style for the label 
label_style = ttk.Style()
label_style.configure('TLabel', foreground='#000000', font=('OCR A Extended', 15))
# style for the entry
entry_style = ttk.Style()
entry_style.configure('TEntry', font=('Dotum', 15))
# style for the button
button_style = ttk.Style()
button_style.configure('TButton', foreground='#000000', font='DotumChe')
# creating a ttk label
url_label = ttk.Label(window, text='Enter Video URL:', style='TLabel')
# creating a ttk entry
url_entry = ttk.Entry(window, width=76, style='TEntry')
# adding the label to the canvas
canvas.create_window(114, 200, window=url_label)
# adding the entry to the canvas
canvas.create_window(250, 230, window=url_entry)
# creating resolution label
resolution_label = Label(window, text='Resolution:')
# adding the label to the canvas
canvas.create_window(50, 260, window=resolution_label)
# creating a combobox to hold the video resolutions
video_resolution = ttk.Combobox(window, width=10)
# adding the combobox to the canvas
canvas.create_window(60, 280, window=video_resolution)
# creating a button for searching resolutions
search_resolution = ttk.Button(window, text='Search Resolution', command=searchThread)
# adding the button to the canvas
canvas.create_window(85, 315, window=search_resolution)
# creating the empty label for displaying download progress
progress_label = Label(window, text='')
# adding the label to the canvas
canvas.create_window(240, 360, window=progress_label)
# creating a progress bar to display progress
progress_bar = ttk.Progressbar(window, orient=HORIZONTAL, length=450, mode='determinate')
# adding the progress bar to the canvas
canvas.create_window(250, 380, window=progress_bar)
# creating the button
download_button = ttk.Button(window, text='Download Video', style='TButton', command=downloadThread)
# adding the button to the canvas
canvas.create_window(240, 410, window=download_button)
# runs the window infinitely
window.mainloop()