Movatterモバイル変換


[0]ホーム

URL:


How to Make a YouTube Video Downloader in Python

Learn how you can build a CLI and GUI YouTube video downloader using Pytube in Python.
  · · 23 min read · Updated nov 2022 ·Web Scraping ·GUI Programming

Want to code faster? OurPython Code Generator lets you create Python scripts with just a few clicks. Try it now!

YouTube is a great platform where you can find tons of great video resources, be it programming, entrepreneurship, movies, etc. It is a platform that also enables users to live stream and watch videos online. You might run into a situation where you want to have some of your favorite YouTube videos on your local computer. To do this, you will need a video downloader for downloading these videos.

If you have ever fancied the idea of building your own YouTube video downloader, then this article is for you; it will show you step-by-step how to build a YouTube video downloader using Python. This article has been split into two sections. In the first section, we will focus on building a command line version, and in the second section, we will build an advanced graphical user interface (GUI) version.

If you want to download only audio from YouTube videos, thenthis tutorial should be helpful.

Table of contents:

We need to install the primary library, which ispytube:

$ pip install pytube

Let us create two new Python files and name them as follows:

Feel free to name them what you prefer, but just make sure the names are meaningful.

Building the Command Line Version

Let's start building the command line version of the app. Open theyoutube_downloader_cli.py file and do the following import:

from pytube import YouTube

Here we are importing the YouTube object frompytube, according topytube’s documentation, it is simply a lightweight library for downloading YouTube videos. That’s all we needed for our imports. Now let us move on to creating a function that will handle the actual video download; we will call itvideo_downloader(). Below the import, paste these lines of code:

# the function takes the video url as an argumentdef 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

Let us break the code down so that we are on the same page; the function takes thevideo_url as the argument, thisvideo_url is then passed to theYouTube() object.

FromYouTube(), we can access video information likestreams,title,thumbnail_url,channel_id, etc., this object is assigned to a variable calledmy_video. So to download the video, we use this set of functions at once,streams.get_highest_resolution().download(); this will download the video in the highest resolution, and finally, we are returning the title of the video.

Thepytube library comes with lots of options for downloading your videos. If you want to download a video of the lowest resolutions, use the:

YouTube(url).streams.first().download()

Or, if you want to specify the video resolution, you can use the below code:

YouTube(url).streams.filter(res="Your_Resolution").first().download()

Just replace the placeholderYour_Resolution with your preferred resolution e.g. 144p, 240p, 360p, 480p, 720p, etc.

Let us make the app prompt the user to enter the video URL. Just below the function, paste the following code:

# the try statement will run if there are no errorstry:    # 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 successfully!!')#the except will catch ValueError, URLError, RegexMatchError and simalarexcept:    print(f'Failed to download video\nThe '\          'following might be the causes\n->No internet '\          'connection\n->Invalid video link')

In the code snippet, we have atry/except block, inside thetry statement, we have the code for getting the URL from the user using theinput() function, and this URL is passed to thevideo_downloader() function to download the video. Inside theexcept statement, we are just printing a message after catching errors.

Now that we have a complete working code, let us test it, copy the URL from any YouTube video and run:

$ python youtube_downloader_cli.py

You will be prompted to provide the video URL, and after entering it, you will get this output:

Enter the YouTube link:https://youtu.be/i9TlSFrsh4QDownloading your Video, please wait......."Find your next Python or ML job with my new job board!" downloaded succussfully!!

If you check in your project’s folder, you will find an mp4 file:

Now re-run the app, and this time, try to provide an invalid link:

Enter the YouTube link:this is my linkDownloading your Video, please wait.......Failed to download videoThe following might be the causes->No internet connection->Invalid video link

Our YouTube video downloader command line version is working perfectly!

Building the GUI Version

In this section, we will build the GUI version of the YouTube video downloader app. This is what we are going to be building:

Designing the User Interface

So without further ado, let us get started with our designing process. The first thing that we will do is import the required libraries, so do the following:

from tkinter import *from tkinter import ttkfrom pytube import YouTubefrom tkinter.messagebox import showinfo, showerrorimport threading

In the code above, we are importing everything fromtkinter, we are also importingttk fromtkinter,ttk is a module that provides styles for the widgets, frompytube we are importing theYouTube object, this will give us access to all the functions available inpytube.

We are also importing theshowinfo andshowerror message boxes, which are popups for displaying messages to the user, and finally, we are importingthreading; this will help our app’s tasks (downloading the video, running the window, and searching for video resolutions) run concurrently.

Now that the imports are taken care of, it is now time we create the main window for the app; just below the imports, paste these lines of code:

from tkinter import *from tkinter import ttkfrom pytube import YouTubefrom tkinter.messagebox import showinfo, showerror, askokcancelimport threading# creates the window using Tk() fucntionwindow = Tk()# creates title for the windowwindow.title('YouTube Video Downloader')# dimensions and position of the windowwindow.geometry('500x460+430+180')# makes the window non-resizablewindow.resizable(height=FALSE, width=FALSE)# runs the window infinitelywindow.mainloop()

Let us break this code down. To create the main window, we are using thetkinter built-inTk() class. After creating the window, we create its title using thetitle() function; then, we give it dimensions and position.

For theheight andwidth we are using 500x460, and for the position that is horizontal and vertical, we are using 430+180, and to make the window non-resizable, we are settingheight andwidth toFALSE, and finally, we are calling themainloop() function, which allows the window to run infinitely until the user clicks the close button.

Now that the window is created, we need to create a container that will contain all the other widgets, so we will use theCanvas widget. Below this line of code:

window.resizable(height=FALSE, width=FALSE)

Paste this code:

# creates the canvas for containing all the widgetscanvas = Canvas(window, width=500, height=400)canvas.pack()

In the code, we are creating a canvas by placing it inside the main window; inside the main window, it will take up awidth of 500 and aheight of 400 like this:

Our container for the widgets is now ready. Let us now start adding widgets to it. The first one will be the YouTube logo. Make sure the logo is located in the same folder asyoutube_downloader_ui.py file:

To create the logo, paste these lines of code:

# loading the logologo = PhotoImage(file='youtubelogo.png')# creates dimensions of the logologo = logo.subsample(10, 10)# adding the logo to the canvascanvas.create_image(250, 80, image=logo)

In the code snippet, we are, first of all, loading the logo using thePhotoImage() function, we are then giving the logo dimensions using thesubsample() function, and finally, we are adding the logo to thecanvas using the functioncreate_image() function, we are positioning the logo 250 horizontally and 80 vertically.

Let us test to see if the logo has been added successfully to thecanvas. This is the output:

The app is taking its shape; we will now define the styles for the widgets that we will be adding just in a moment; just below the logo code, paste this code:

"""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 entryentry_style = ttk.Style()entry_style.configure('TEntry', font=('Dotum', 15))# style for the buttonbutton_style = ttk.Style()button_style.configure('TButton', foreground='#000000', font='DotumChe')

Let’s break down the code snippet above; we are creating styles for the widgets; we are using thettk module for this.

We are creating a style object using thettk.Style() class, so to add styles to the object, we will use theconfigure() function, this function takes the style name; in our case, we haveTLabel,TButton, andTEntry as style names,foreground, andfont() are the other arguments theStyle() class is taking.

In our widgets, we will use the style names to refer to or point to the style we want to use. Now that the styles are taken care of, we will now create and add the first two widgets to the canvas, the label and the entry. Below the styles, add this code:

# creating a ttk labelurl_label = ttk.Label(window, text='Enter Video URL:', style='TLabel')# creating a ttk entryurl_entry = ttk.Entry(window, width=76, style='TEntry')# adding the label to the canvascanvas.create_window(114, 200, window=url_label)# adding the entry to the canvascanvas.create_window(250, 230, window=url_entry)

To create a styled label, we are using attk.Label() class that comes from thettk module, this function takeswindow,text, and thestyle as arguments, and to create a styled entry, we are using thettk.Entry() class, also has arguments,window,width, andstyle.

Now to add these widgets to thecanvas we are using thecreate_window() canvas function, takes two integers, in our case, 114 and 200 for the label and 250 and 230 for the entry; all these are for positioning the widget.

Running the app, we will get this output:

Making the Resolution Combobox

Now we should add the resolution label and theCombobox that will hold the video resolutions; the two will come below the entry widget; add this code:

# creating resolution labelresolution_label = Label(window, text='Resolution:')# adding the label to the canvascanvas.create_window(50, 260, window=resolution_label)# creating a combobox to hold the video resolutionsvideo_resolution = ttk.Combobox(window, width=10)# adding the combobox to the canvascanvas.create_window(60, 280, window=video_resolution)

We are creating two widgets; the first one is the label for displaying the textResolution, something worth noticing here, this label is not attk label. After the label, we create the actualCombobox, it takeswindow, andwidth as arguments, and finally, we add all these to thecanvas as usual. With the above code added, this is what we get:

Currently, theCombobox is empty, and something worth your attention about theCombobox; you can enter values as well, just in case the value you want is not in theCombobox.

Since we want to search for video resolutions, we will add a button for the search feature. To do that, add the following code below theCombobox:

# creating a button for searching resolutionssearch_resolution = ttk.Button(window, text='Search Resolution')# adding the button to the canvascanvas.create_window(85, 315, window=search_resolution)

With the above code added, this is the output:

Creating the Progress Bar & Download Button

Let us finish off the GUI design by adding our last three widgets, the empty label, the progress bar, and theDownload Video button; the empty label and the progress bar will be used for displaying the video download progress. After theSearch Resolution button, add this code:

# creating the empty label for displaying download progressprogress_label = Label(window, text='')# adding the label to the canvascanvas.create_window(240, 360, window=progress_label)# creating a progress bar to display progressprogress_bar = ttk.Progressbar(window, orient=HORIZONTAL, length=450, mode='determinate')# adding the progress bar to the canvascanvas.create_window(250, 380, window=progress_bar)# creating the buttondownload_button = ttk.Button(window, text='Download Video', style='TButton')# adding the button to the canvascanvas.create_window(240, 410, window=download_button)

Here we are creating an empty label, a progress bar, and a styled button using theLabel(),ttk.Progressbar() andttk.Button() functions, to the progress bar before adding it to the canvas, we are passing thewindow,orient,length, andmode as arguments.

To theprogress_label, we are addingwindow and an empty text, and to the button widget, we are also passing the argumentswindow,text, andstyle then we add all these widgets to thecanvas.

This is what we get after adding the empty label, the progress bar, and the button:

Congrats on successfully designing the app’s GUI!

Implementing the Search Video Resolution Functionality

Now it is time we implement the search video resolution functionality. For this, we need to create a function for doing that, so after the imports, paste this code:

# function for searching video resolutionsdef 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')

Let us break the code down so that we are on the same page. First of all, we get the video link from the entry. Then we check if the link entry is empty. If it’s empty, we notify the user to provide the link. Otherwise, we create aYouTube() object; then an emptyresolutions list containing all the video resolutions.

Then we have afor loop that’s looping through all the video resolutions filtered byfile_extension argument, then all these video resolutions are added to theresolutions list via this line of code:

resolutions.append(i.resolution)

And to add all the video streams to the emptyCombobox we created earlier; we are using this code:

video_resolution['values'] = resolutions

After the search is complete, we notify the user by theshowinfo popup. And finally, in theexcept statement, we are catching all the errors, and the message will be displayed using theshowerror popup.

We want to run thesearchResolution() function in a separate thread, so to do that, we will create another function that will point to this function and run it as a thread. Below thesearchResolution() function, paste this code:

# the function to run the searchResolution function as a threaddef searchThread():    t1 = threading.Thread(target=searchResolution)    t1.start()

The above code createssearchThread() function, and inside it, we are creating at1 thread whose target function is thesearchResolution(), and then the thread is started.

So to test this feature, we will have to bind thesearchThread() function to theSearch Resolution button, edit thesearch_resolution button code and make it look like this:

search_resolution = ttk.Button(window, text='Search Resolution', command=searchThread)

We are using thecommand argument to let the button know which function to trigger when the user clicks it. The function we are pointing to is thesearchThread(), run the app paste the video URL, and click theSearch Resolution button. The first output is this:

And the second output will be this after clicking theCombobox:

Now we have managed to add all the searched video resolutions to theCombobox.

Related: How to Make a YouTube Audio Downloader in Python.

Implementing the Video Download and Threading Functionality

In this final part of the article, we will implement the video download functionality. Just above thesearchResolution() function, paste this code:

# the function to download the videodef 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 and formatted file size                    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

In this code, we are creating a function calleddownload_video() and inside the function, we have an outertry/except block and an innertry/except block that’s wrapped inside theelse statement. So we are getting the video URL and resolution from the entry and theCombobox respectively using theget() function, then we are doing some data validation using theif andelif statements and show error windows accordingly.

If the input is validated, here we have the innertry/except block, and inside thetry statement, we have a progress tracking function calledon_progress() whose arguments arestream,chunk, andbytes_remaining.

Inside this function, we calculate the bytes downloaded and the percentage completed, which is then added to the progress bar and the empty progress label. Finally, we are updating the window with theupdate() function.

Theget_formatted_size() function inside theon_progress() function calculates and returns the video size in the format KBs, MBs, GBs, TBs, etc. It was grabbed fromthis tutorial.

Just outside theon_progress() function, we are creating theYouTube() object, which takes the video URL and theon_progress() function as arguments, then the actual video download is handled by this line of code:

video.streams.filter(res=resolution).first().download()

After the video is downloaded successfully, the success message pops up, and if they are errors like a broken internet connection, theexcept statement will handle all this and notify the user of the errors as well, and both the progress bar and progress label are reset.

Finally, we have the final outerexcept statement, this will handle errors like invalid video URL, no internet connection, etc., and again reset both the progress bar and the progress label.

After taking care of thedownload_video() function, we will again create another function that will execute this function as a thread, so below thesearchThread() function paste this code:

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

Here we are doing the same thing as in thesearchThread() function, we are creating a thread whose target isdownload_video() function, then the thread is started using thestart() function.

Now to download the video, we need to bind thedownloadThread() function with theDownload Video button, so to do that, add thecommand argument, and this command must point to thedownloadThread() function as below:

download_button = ttk.Button(window, text='Download Video', style='TButton', command=downloadThread)

So this is what it means when the user clicks theDownload Video button, thedownloadThread() function will be triggered and start thedownload_video() function as a thread, this means that tasks will run without conflicts. This comes in handy because the app will run smoothly without glitches like the app not responding.

Let us test the app, run it and paste the video URL in the entry, search for the video resolution, and after a successful search, choose the resolution and click theDownload Video button, wait for the video to download, and you will get this output:

Check in your project’s folder to confirm if the video has been downloaded successfully, you will get this:

If you enter the video URL, search for the video resolution, and leave theCombobox empty, you will get this output:

If both the URL entry and the resolutionCombobox are left empty, and theDownload Video button is clicked. This is the output:

If you want to search for video resolution without providing the video URL, you will get this:

And finally, if your video download was in progress and you had an unstable or broken internet connection, the output would be as follows:

The app is working perfectly, but something to note here as you are using it, it’s not every video that you might be able to download using the app because some YouTube videos are private, some are blocked in a certain region, some are age-restricted, and some require membership. You can find the list ofpytube exceptionshere.

Conclusion

We hope you enjoyed the article on building a YouTube video downloader using Python. This is a great out-of-the-box solution for downloading your favorite YouTube videos offline. We have not covered everything considering what thepytube library can do, but we believe that the knowledge you have gained can be used to build something meaningful in your future projects.

You can get the complete codehere.

Learn also:How to Extract YouTube Data using YouTube API in Python.

Happy coding ♥

Want to code smarter? OurPython Code Assistant is waiting to help you. Try it now!

View Full Code Analyze My Code
Sharing is caring!



Read Also


How to Extract YouTube Data using YouTube API in Python
How to Extract YouTube Data in Python
How to Make a YouTube Audio Downloader in Python

Comment panel

    Got a coding query or need some guidance before you comment? Check out thisPython Code Assistant for expert advice and handy tips. It's like having a coding tutor right in your fingertips!





    Ethical Hacking with Python EBook - Topic - Top


    Join 50,000+ Python Programmers & Enthusiasts like you!



    Tags

    Ethical Hacking with Python EBook - Topic - Middle


    New Tutorials

    Popular Tutorials


    Ethical Hacking with Python EBook - Topic - Bottom






    Claim your Free Chapter!

    Download a Completely Free Practical Python PDF Processing Chapter.

    See how the book can help you build handy PDF Processing tools!



    [8]ページ先頭

    ©2009-2025 Movatter.jp