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