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.
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!
In this section, we will build the GUI version of the YouTube video downloader app. This is what we are going to be building:
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:
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:
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!
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.
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.
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 CodeGot 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!