1+ from tkinter import *
2+ from tkinter import ttk
3+ from pytube import YouTube
4+ from tkinter .messagebox import showinfo , showerror , askokcancel
5+ import threading
6+
7+
8+
9+ # the function to download the video
10+ def download_video ():
11+ # the try statement to excute the download the video code
12+ try :
13+ # getting video url from entry
14+ video_link = url_entry .get ()
15+ # getting video resolution from Combobox
16+ resolution = video_resolution .get ()
17+ # checking if the entry and combobox is empty
18+ if resolution == '' and video_link == '' :
19+ # display error message when combobox is empty
20+ showerror (title = 'Error' , message = 'Please enter both the video URL and resolution!!' )
21+ # checking if the resolution is empty
22+ elif resolution == '' :
23+ # display error message when combobox is empty
24+ showerror (title = 'Error' , message = 'Please select a video resolution!!' )
25+ # checking if the comboxbox value is None
26+ elif resolution == 'None' :
27+ # display error message when combobox value is None
28+ showerror (title = 'Error' , message = 'None is an invalid video resolution!!\n ' \
29+ 'Please select a valid video resolution' )
30+ # else let's download the video
31+ else :
32+ # this try statement will run if the resolution exists for the video
33+ try :
34+ # this function will track the video download progress
35+ def on_progress (stream , chunk , bytes_remaining ):
36+ # the total size of the video
37+ total_size = stream .filesize
38+ # this function will get the size of the video
39+ def get_formatted_size (total_size , factor = 1024 , suffix = 'B' ):
40+ # looping through the units
41+ for unit in ["" , "K" , "M" , "G" , "T" , "P" , "E" , "Z" ]:
42+ if total_size < factor :
43+ return f"{ total_size :.2f} { unit } { suffix } "
44+ total_size /= factor
45+ # returning the formatted video size
46+ return f"{ total_size :.2f} Y{ suffix } "
47+
48+ # getting the formatted video size calling the function
49+ formatted_size = get_formatted_size (total_size )
50+ # the size downloaded after the start
51+ bytes_downloaded = total_size - bytes_remaining
52+ # the percentage downloaded after the start
53+ percentage_completed = round (bytes_downloaded / total_size * 100 )
54+ # updating the progress bar value
55+ progress_bar ['value' ] = percentage_completed
56+ # updating the empty label with the percentage value
57+ progress_label .config (text = str (percentage_completed ) + '%, File size:' + formatted_size )
58+ # updating the main window of the app
59+ window .update ()
60+
61+ # creating the YouTube object and passing the the on_progress function
62+ video = YouTube (video_link , on_progress_callback = on_progress )
63+ # downlaoding the actual video
64+ video .streams .filter (res = resolution ).first ().download ()
65+ # popup for dispalying the video downlaoded success message
66+ showinfo (title = 'Download Complete' , message = 'Video has been downloaded successfully.' )
67+ # ressetting the progress bar and the progress label
68+ progress_label .config (text = '' )
69+ progress_bar ['value' ] = 0
70+ # the except will run when the resolution is not available or invalid
71+ except :
72+ showerror (title = 'Download Error' , message = 'Failed to download video for this resolution' )
73+ # ressetting the progress bar and the progress label
74+ progress_label .config (text = '' )
75+ progress_bar ['value' ] = 0
76+
77+ # the except statement to catch errors, URLConnectError, RegMatchError
78+ except :
79+ # popup for displaying the error message
80+ showerror (title = 'Download Error' , message = 'An error occurred while trying to ' \
81+ 'download the video\n The following could ' \
82+ 'be the causes:\n ->Invalid link\n ->No internet connection\n ' \
83+ 'Make sure you have stable internet connection and the video link is valid' )
84+ # ressetting the progress bar and the progress label
85+ progress_label .config (text = '' )
86+ progress_bar ['value' ] = 0
87+
88+
89+
90+ # function for searching video resolutions
91+ def searchResolution ():
92+ # getting video url from entry
93+ video_link = url_entry .get ()
94+ # checking if the video link is empty
95+ if video_link == '' :
96+ showerror (title = 'Error' , message = 'Provide the video link please!' )
97+ # if video link not empty search resolution
98+ else :
99+ try :
100+ # creating a YouTube object
101+ video = YouTube (video_link )
102+ # an empty list that will hold all the video resolutions
103+ resolutions = []
104+ # looping through the video streams
105+ for i in video .streams .filter (file_extension = 'mp4' ):
106+ # adding the video resolutions to the resolutions list
107+ resolutions .append (i .resolution )
108+ # adding the resolutions to the combobox
109+ video_resolution ['values' ] = resolutions
110+ # when search is complete notify the user
111+ showinfo (title = 'Search Complete' , message = 'Check the Combobox for the available video resolutions' )
112+ # catch any errors if they occur
113+ except :
114+ # notify the user if errors are caught
115+ showerror (title = 'Error' , message = 'An error occurred while searching for video resolutions!\n ' \
116+ 'Below might be the causes\n ->Unstable internet connection\n ->Invalid link' )
117+
118+
119+
120+
121+
122+ # the function to run the searchResolution function as a thread
123+ def searchThread ():
124+ t1 = threading .Thread (target = searchResolution )
125+ t1 .start ()
126+
127+
128+ # the function to run the download_video function as a thread
129+ def downloadThread ():
130+ t2 = threading .Thread (target = download_video )
131+ t2 .start ()
132+
133+
134+
135+
136+ # creates the window using Tk() fucntion
137+ window = Tk ()
138+
139+ # creates title for the window
140+ window .title ('YouTube Video Downloader' )
141+ # dimensions and position of the window
142+ window .geometry ('500x460+430+180' )
143+ # makes the window non-resizable
144+ window .resizable (height = FALSE , width = FALSE )
145+
146+ # creates the canvas for containing all the widgets
147+ canvas = Canvas (window , width = 500 , height = 400 )
148+ canvas .pack ()
149+
150+ # loading the logo
151+ logo = PhotoImage (file = 'youtubelogo.png' )
152+ # creates dimensions of the logo
153+ logo = logo .subsample (10 , 10 )
154+ # adding the logo to the canvas
155+ canvas .create_image (250 , 80 , image = logo )
156+
157+
158+ """Styles for the widgets"""
159+ # style for the label
160+ label_style = ttk .Style ()
161+ label_style .configure ('TLabel' , foreground = '#000000' , font = ('OCR A Extended' , 15 ))
162+
163+ # style for the entry
164+ entry_style = ttk .Style ()
165+ entry_style .configure ('TEntry' , font = ('Dotum' , 15 ))
166+
167+ # style for the button
168+ button_style = ttk .Style ()
169+ button_style .configure ('TButton' , foreground = '#000000' , font = 'DotumChe' )
170+
171+
172+ # creating a ttk label
173+ url_label = ttk .Label (window , text = 'Enter Video URL:' , style = 'TLabel' )
174+ # creating a ttk entry
175+ url_entry = ttk .Entry (window , width = 76 , style = 'TEntry' )
176+
177+ # adding the label to the canvas
178+ canvas .create_window (114 , 200 , window = url_label )
179+ # adding the entry to the canvas
180+ canvas .create_window (250 , 230 , window = url_entry )
181+
182+
183+ # creating resolution label
184+ resolution_label = Label (window , text = 'Resolution:' )
185+ # adding the label to the canvas
186+ canvas .create_window (50 , 260 , window = resolution_label )
187+
188+
189+ # creating a combobox to hold the video resolutions
190+ video_resolution = ttk .Combobox (window , width = 10 )
191+ # adding the combobox to the canvas
192+ canvas .create_window (60 , 280 , window = video_resolution )
193+
194+
195+ # creating a button for searching resolutions
196+ search_resolution = ttk .Button (window , text = 'Search Resolution' , command = searchThread )
197+ # adding the button to the canvas
198+ canvas .create_window (85 , 315 , window = search_resolution )
199+
200+
201+ # creating the empty label for displaying download progress
202+ progress_label = Label (window , text = '' )
203+ # adding the label to the canvas
204+ canvas .create_window (240 , 360 , window = progress_label )
205+
206+ # creating a progress bar to display progress
207+ progress_bar = ttk .Progressbar (window , orient = HORIZONTAL , length = 450 , mode = 'determinate' )
208+ # adding the progress bar to the canvas
209+ canvas .create_window (250 , 380 , window = progress_bar )
210+
211+ # creating the button
212+ download_button = ttk .Button (window , text = 'Download Video' , style = 'TButton' , command = downloadThread )
213+ # adding the button to the canvas
214+ canvas .create_window (240 , 410 , window = download_button )
215+
216+
217+ # runs the window infinitely
218+ window .mainloop ()
0 commit comments