From 6eee817078af7612bbe8bb0c7d61f9858bdd65ba Mon Sep 17 00:00:00 2001 From: senthilnathan Date: Thu, 20 Oct 2022 20:39:20 +0530 Subject: [PATCH 1/6] added Downtube --- PYTHON APPS/DownTube/Exceptions.py | 18 +++ PYTHON APPS/DownTube/Functionality.py | 138 ++++++++++++++++++ .../DownTube/Props/YT_icon_ico_64x64.ico | Bin 0 -> 11942 bytes .../DownTube/Props/YT_icon_png_64x64.png | Bin 0 -> 36544 bytes PYTHON APPS/DownTube/README.md | 35 +++++ PYTHON APPS/DownTube/downtube.py | 101 +++++++++++++ PYTHON APPS/DownTube/requirements.txt | Bin 0 -> 56 bytes 7 files changed, 292 insertions(+) create mode 100644 PYTHON APPS/DownTube/Exceptions.py create mode 100644 PYTHON APPS/DownTube/Functionality.py create mode 100644 PYTHON APPS/DownTube/Props/YT_icon_ico_64x64.ico create mode 100644 PYTHON APPS/DownTube/Props/YT_icon_png_64x64.png create mode 100644 PYTHON APPS/DownTube/README.md create mode 100644 PYTHON APPS/DownTube/downtube.py create mode 100644 PYTHON APPS/DownTube/requirements.txt diff --git a/PYTHON APPS/DownTube/Exceptions.py b/PYTHON APPS/DownTube/Exceptions.py new file mode 100644 index 00000000..aae18fbc --- /dev/null +++ b/PYTHON APPS/DownTube/Exceptions.py @@ -0,0 +1,18 @@ +class Downtube_Error(Exception): + """ This is the base exception class for all other user defined exception(classes) """ + pass + +class Link_Error(Downtube_Error): + """ When there is no Link is given as input """ + pass + +class InvalidLink(Downtube_Error): + """ When the ink URL isn't a youtube link """ + pass + +class DirectoryError(Downtube_Error): + """ when No directory is given """ + pass +class ResolutionError(Downtube_Error): + """ when no resolution is givenn """ + pass \ No newline at end of file diff --git a/PYTHON APPS/DownTube/Functionality.py b/PYTHON APPS/DownTube/Functionality.py new file mode 100644 index 00000000..868e27a6 --- /dev/null +++ b/PYTHON APPS/DownTube/Functionality.py @@ -0,0 +1,138 @@ +import re +from tkinter.constants import END +import downtube as dt +import Exceptions as ex +from tkinter import messagebox as msg +from tkinter import filedialog as fd +import urllib.request +import pytube as pt +from pytube import YouTube + + +def clear(link, get_link): + """ Clears the Entry box widget """ + download_link = link.get() + if download_link == "": + msg.showwarning( + title= "Warning", + message= "Warning: The input field is already empty", + ) + else : + get_link.delete(0,last= len(download_link)) + +def browse_folder(get_dir): + global dir_path + dir_path = fd.askdirectory() + get_dir.delete(0, END) + get_dir.configure(fg= "black") + get_dir.insert(0, dir_path) + if dir_path == "": + get_dir.configure(fg= "grey") + get_dir.insert(0, "Choose a folder") + +def check_connectivity(link): # Referred website: codespeedy (https://www.codespeedy.com/how-to-check-the-internet-connection-in-python/) + """ Checks for internet connectivity or valid youtube link """ + try: + urllib.request.urlopen(link) + except: + msg.showerror( + title= "Network Error", + message= "Connectivity issue found", + detail= "Check you internet connectivity \nor\n the link might not be correct" + ) + +def dwn(download_link, video_resolution, directory): + """ Downloads the video """ + yt = pt.YouTube(download_link) + try: + if video_resolution == "": + raise ex.ResolutionError + except ex.ResolutionError: + msg.showerror(title= "Downtube", + message= "No video resolution found" + ) + if video_resolution == "360p": + try: + video = yt.streams.filter(progressive= True, file_extension= "mp4", res= "360p", type= "video").first() + if video == None: + raise AttributeError + else: + return video + #video.download(output_path= directory) + except AttributeError: + msg.showinfo(title= "Downtube", + message= "The video resolution is not available to be downloaded ", + detail= "Try downloading with other resolution" + ) + elif video_resolution == "720p": + try: + video = yt.streams.filter(progressive= True, file_extension= "mp4", res= "720p", type= "video").first() + if video == None: + raise AttributeError + else: + return video + #video.download(output_path= directory) + except AttributeError: + msg.showinfo(title= "Downtube", + message= "The video resolution is not available to be downloaded ", + detail= "Try downloading with other resolution" + ) + +def clear_inputs(get_link, get_resolution): + input_of_get_link = get_link.get() + input_of_get_resolution = get_resolution.get() + get_link.delete(0,last= len(input_of_get_link)) + get_resolution.delete(0,last= len(input_of_get_resolution)) + + + +def download_bt(download_link, get_link, video_resolution, directory, get_dir, resolution_box ): + """ All the process for downloading will be done here """ + verify_YT = re.search(r"youtube.com", download_link) # Verifying youtube link or not + + + try: + """ Checks for youtube link and rasie error if link is not valid or not found """ + if download_link == "": + raise ex.Link_Error + elif verify_YT == None: + raise ex.InvalidLink + else: + check_connectivity(download_link) + get_link.delete(0,last= len(download_link)) + try: + if directory == "": + raise ex.DirectoryError + else: + vid = dwn(download_link, video_resolution, directory) + vid.download(directory) #type: ignore + clear_inputs(get_dir, resolution_box) + except ex.DirectoryError: + msg.showerror(title= "Directory error", + message= "Directory Error: No Directory is found", + detail= "Browse or enter the directory to save the file" + ) + except ex.Link_Error: + msg.showerror( + title= "Link Error", + message= "Youtube link Not Found", + detail= "You've not given any link in the \"Link section\"" + ) + except ex.InvalidLink: + msg.showerror( + title= "Invalid link", + message= "Error: Invalid link", + detail= "The given link is invalid or doesn't belongs to youtube" + ) + + try: + if directory == "Choose a folder" : + raise ex.DirectoryError + except ex.DirectoryError: + msg.showerror( + title = "Directory Error", + message="Error: No directory is given to save the file" + ) + +if __name__=='__main__': + dt.main() diff --git a/PYTHON APPS/DownTube/Props/YT_icon_ico_64x64.ico b/PYTHON APPS/DownTube/Props/YT_icon_ico_64x64.ico new file mode 100644 index 0000000000000000000000000000000000000000..7b5b87ab79acb5c92452645d980559151dba1eb7 GIT binary patch literal 11942 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 literal 0 HcmV?d00001 diff --git a/PYTHON APPS/DownTube/README.md b/PYTHON APPS/DownTube/README.md new file mode 100644 index 00000000..0902daf4 --- /dev/null +++ b/PYTHON APPS/DownTube/README.md @@ -0,0 +1,35 @@ +# DownTube + +## Introduction + +**DownTube** is a piece of GUI based program which downloads Youtube videos either in 360p or 720p resolution. Very simple User Interface. + +--- + +## Packages + +Refer [requirements.txt](requirements.txt) for python packages used to develop this program. + +--- + +## Features + +**Features of this application:** + +* Just paste the complete URL(***from search bar of the browser***) of the video you want to download. + +* You can store the downloaded video whereever you want(***just click browse button***) + +* Choose the available resolution(either 360p or 720p). + +--- + +## Information to users + +* If videos aren't downloaded, Try to update or reinstall `pytube` package. Since pytube had issues in downloading the videos from YouTube in the past. + +* If this program shows not responding in windows, kindly don't close the application. Since this the sign of downloading the videos. + +* This program was tested on Windows machines and works completely fine. But it may or may not work on other operating system platforms like(Linux or MacOS, etc.,). + +--- diff --git a/PYTHON APPS/DownTube/downtube.py b/PYTHON APPS/DownTube/downtube.py new file mode 100644 index 00000000..51cb9c95 --- /dev/null +++ b/PYTHON APPS/DownTube/downtube.py @@ -0,0 +1,101 @@ +import tkinter as tk +from tkinter import ttk +from tkinter.constants import RAISED + +# Importing the user defined module. +import Functionality as fn + +def main(): + + # Root window + main_window = tk.Tk() + main_window.title("Downtube") # Title of the window + main_window.minsize(600, 400) # Default Window size + main_window.rowconfigure(0,weight=1) # Rowconfiguration of root window in order to expand widgets, when window is is resized. + main_window.columnconfigure(0, weight= 1) # Rowconfiguration of root window in order to expand widgets, when window is is resized. + main_window.rowconfigure(1,weight=1) # Rowconfiguration of root window in order to expand widgets, when window is is resized. ### + main_window.columnconfigure(1, weight= 1) # Rowconfiguration of root window in order to expand widgets, when window is is resized. ### + main_window.configure(bg= "white") # Background color for root window + +# Parent Frame widgets: + + # Input frame widget. + main_frame = tk.Frame(main_window, borderwidth= 2,bg= "white") + main_frame.grid(row= 0, column= 0, columnspan= 3, rowspan= 4) + + # Configuration of row and column of "main_frame" in order to expand widgets, when window is is resized. + main_frame.columnconfigure(0, weight= 1) + main_frame.columnconfigure(1, weight= 1) + main_frame.columnconfigure(2, weight= 1) + main_frame.rowconfigure(0, weight= 1) + main_frame.rowconfigure(1, weight= 1) + main_frame.rowconfigure(2, weight= 1) + main_frame.rowconfigure(3, weight= 1) + +# main_frame widgets + + # Display label indicating --> 'paste the youtube link'. + linke_label = tk.Label(main_frame, text= "Paste the YouTube link", width= 40, borderwidth= 1, anchor= "w", bg= "white") + linke_label.grid(row= 0, column= 1, sticky="WE", pady= 2) + + # Display label indicating --> 'Browse to save the file'. + linke_label = tk.Label(main_frame, text= "Browse to save the file", bg= "white", width= 40, anchor= "w") + linke_label.grid(row= 2, column= 1) + + # Display label indicating --> 'Choose the resolution'. + resolution_lb = tk.Label( + main_frame, text= "Choose the resolution", width= 15, + height= 1, anchor= "w", bg= "white" + ) + resolution_lb.grid(row=4, column= 1, pady=2, sticky= "we") + + + # Entry Widget --> Getting youtube link. + link = tk.StringVar() + get_link = tk.Entry(main_frame, textvariable= link, bg= "white") + get_link.grid(row= 1, column= 1, sticky= "wE", pady= 2) + + # Entry Widget --> Getting Directory to save file. + directory = tk.StringVar() + get_dir = tk.Entry(main_frame,textvariable= directory, bg= "white", fg= "grey") + get_dir.grid(row= 3, column= 1, sticky= "wE") + get_dir.insert(0, "Choose a folder") + + # Combo box Widget --> Shows the options(Resolution) available. + my_string_var = tk.StringVar() + resolution_box = ttk.Combobox( + main_frame, textvariable=my_string_var, + values=["360p", "720p"]) + resolution_box.grid(row=5, column= 1, pady=2, sticky= "we") + + # Button widget --> Clear the Input field of youtube entry widget + clear_bt = tk.Button( + main_frame, text= "Clear", + width= 10, height= 1, + bg= "grey", + command= lambda: fn.clear(link, get_link) + ) + clear_bt.grid(row= 1, column= 2, pady= 2) + + # Button widget --> Opens file explorer to save the file + temp_bt = tk.Button( + main_frame, text= "Browse", + relief= RAISED, width= 10, + height= 1, bg= "grey", + command= lambda :fn.browse_folder(get_dir) + ) + temp_bt.grid(row=3, column= 2, pady=2) + + # Button widget --> Downloads the video + download_file = tk.Button( + main_frame, text= "Download", + relief= RAISED, width= 10, + height= 1, bg= "grey", anchor= "center", + command= lambda :fn.download_bt(link.get(), get_link, resolution_box.get(), directory.get(), get_dir, resolution_box) + ) + download_file.grid(row=5, column= 2, pady=2) + + main_window.mainloop() + +if __name__ == '__main__': + main() diff --git a/PYTHON APPS/DownTube/requirements.txt b/PYTHON APPS/DownTube/requirements.txt new file mode 100644 index 0000000000000000000000000000000000000000..0ff08030d864dc8c097638cae7a5ac3f610fde01 GIT binary patch literal 56 tcmezWuYjSFp@gB7A&DWC!4?P&8H^b8fTSLS0Rt}s7f>!6EN*}z3II)42&Di3 literal 0 HcmV?d00001 From 77f9e007db91b390b4aeeefda7df0e4327c0409d Mon Sep 17 00:00:00 2001 From: senthilnathan Date: Thu, 20 Oct 2022 20:44:07 +0530 Subject: [PATCH 2/6] removed DownTube --- PYTHON APPS/DownTube/Exceptions.py | 18 --- PYTHON APPS/DownTube/Functionality.py | 138 ------------------ .../DownTube/Props/YT_icon_ico_64x64.ico | Bin 11942 -> 0 bytes .../DownTube/Props/YT_icon_png_64x64.png | Bin 36544 -> 0 bytes PYTHON APPS/DownTube/README.md | 35 ----- PYTHON APPS/DownTube/downtube.py | 101 ------------- PYTHON APPS/DownTube/requirements.txt | Bin 56 -> 0 bytes 7 files changed, 292 deletions(-) delete mode 100644 PYTHON APPS/DownTube/Exceptions.py delete mode 100644 PYTHON APPS/DownTube/Functionality.py delete mode 100644 PYTHON APPS/DownTube/Props/YT_icon_ico_64x64.ico delete mode 100644 PYTHON APPS/DownTube/Props/YT_icon_png_64x64.png delete mode 100644 PYTHON APPS/DownTube/README.md delete mode 100644 PYTHON APPS/DownTube/downtube.py delete mode 100644 PYTHON APPS/DownTube/requirements.txt diff --git a/PYTHON APPS/DownTube/Exceptions.py b/PYTHON APPS/DownTube/Exceptions.py deleted file mode 100644 index aae18fbc..00000000 --- a/PYTHON APPS/DownTube/Exceptions.py +++ /dev/null @@ -1,18 +0,0 @@ -class Downtube_Error(Exception): - """ This is the base exception class for all other user defined exception(classes) """ - pass - -class Link_Error(Downtube_Error): - """ When there is no Link is given as input """ - pass - -class InvalidLink(Downtube_Error): - """ When the ink URL isn't a youtube link """ - pass - -class DirectoryError(Downtube_Error): - """ when No directory is given """ - pass -class ResolutionError(Downtube_Error): - """ when no resolution is givenn """ - pass \ No newline at end of file diff --git a/PYTHON APPS/DownTube/Functionality.py b/PYTHON APPS/DownTube/Functionality.py deleted file mode 100644 index 868e27a6..00000000 --- a/PYTHON APPS/DownTube/Functionality.py +++ /dev/null @@ -1,138 +0,0 @@ -import re -from tkinter.constants import END -import downtube as dt -import Exceptions as ex -from tkinter import messagebox as msg -from tkinter import filedialog as fd -import urllib.request -import pytube as pt -from pytube import YouTube - - -def clear(link, get_link): - """ Clears the Entry box widget """ - download_link = link.get() - if download_link == "": - msg.showwarning( - title= "Warning", - message= "Warning: The input field is already empty", - ) - else : - get_link.delete(0,last= len(download_link)) - -def browse_folder(get_dir): - global dir_path - dir_path = fd.askdirectory() - get_dir.delete(0, END) - get_dir.configure(fg= "black") - get_dir.insert(0, dir_path) - if dir_path == "": - get_dir.configure(fg= "grey") - get_dir.insert(0, "Choose a folder") - -def check_connectivity(link): # Referred website: codespeedy (https://www.codespeedy.com/how-to-check-the-internet-connection-in-python/) - """ Checks for internet connectivity or valid youtube link """ - try: - urllib.request.urlopen(link) - except: - msg.showerror( - title= "Network Error", - message= "Connectivity issue found", - detail= "Check you internet connectivity \nor\n the link might not be correct" - ) - -def dwn(download_link, video_resolution, directory): - """ Downloads the video """ - yt = pt.YouTube(download_link) - try: - if video_resolution == "": - raise ex.ResolutionError - except ex.ResolutionError: - msg.showerror(title= "Downtube", - message= "No video resolution found" - ) - if video_resolution == "360p": - try: - video = yt.streams.filter(progressive= True, file_extension= "mp4", res= "360p", type= "video").first() - if video == None: - raise AttributeError - else: - return video - #video.download(output_path= directory) - except AttributeError: - msg.showinfo(title= "Downtube", - message= "The video resolution is not available to be downloaded ", - detail= "Try downloading with other resolution" - ) - elif video_resolution == "720p": - try: - video = yt.streams.filter(progressive= True, file_extension= "mp4", res= "720p", type= "video").first() - if video == None: - raise AttributeError - else: - return video - #video.download(output_path= directory) - except AttributeError: - msg.showinfo(title= "Downtube", - message= "The video resolution is not available to be downloaded ", - detail= "Try downloading with other resolution" - ) - -def clear_inputs(get_link, get_resolution): - input_of_get_link = get_link.get() - input_of_get_resolution = get_resolution.get() - get_link.delete(0,last= len(input_of_get_link)) - get_resolution.delete(0,last= len(input_of_get_resolution)) - - - -def download_bt(download_link, get_link, video_resolution, directory, get_dir, resolution_box ): - """ All the process for downloading will be done here """ - verify_YT = re.search(r"youtube.com", download_link) # Verifying youtube link or not - - - try: - """ Checks for youtube link and rasie error if link is not valid or not found """ - if download_link == "": - raise ex.Link_Error - elif verify_YT == None: - raise ex.InvalidLink - else: - check_connectivity(download_link) - get_link.delete(0,last= len(download_link)) - try: - if directory == "": - raise ex.DirectoryError - else: - vid = dwn(download_link, video_resolution, directory) - vid.download(directory) #type: ignore - clear_inputs(get_dir, resolution_box) - except ex.DirectoryError: - msg.showerror(title= "Directory error", - message= "Directory Error: No Directory is found", - detail= "Browse or enter the directory to save the file" - ) - except ex.Link_Error: - msg.showerror( - title= "Link Error", - message= "Youtube link Not Found", - detail= "You've not given any link in the \"Link section\"" - ) - except ex.InvalidLink: - msg.showerror( - title= "Invalid link", - message= "Error: Invalid link", - detail= "The given link is invalid or doesn't belongs to youtube" - ) - - try: - if directory == "Choose a folder" : - raise ex.DirectoryError - except ex.DirectoryError: - msg.showerror( - title = "Directory Error", - message="Error: No directory is given to save the file" - ) - -if __name__=='__main__': - dt.main() diff --git a/PYTHON APPS/DownTube/Props/YT_icon_ico_64x64.ico b/PYTHON APPS/DownTube/Props/YT_icon_ico_64x64.ico deleted file mode 100644 index 7b5b87ab79acb5c92452645d980559151dba1eb7..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 11942 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 diff --git a/PYTHON APPS/DownTube/README.md b/PYTHON APPS/DownTube/README.md deleted file mode 100644 index 0902daf4..00000000 --- a/PYTHON APPS/DownTube/README.md +++ /dev/null @@ -1,35 +0,0 @@ -# DownTube - -## Introduction - -**DownTube** is a piece of GUI based program which downloads Youtube videos either in 360p or 720p resolution. Very simple User Interface. - ---- - -## Packages - -Refer [requirements.txt](requirements.txt) for python packages used to develop this program. - ---- - -## Features - -**Features of this application:** - -* Just paste the complete URL(***from search bar of the browser***) of the video you want to download. - -* You can store the downloaded video whereever you want(***just click browse button***) - -* Choose the available resolution(either 360p or 720p). - ---- - -## Information to users - -* If videos aren't downloaded, Try to update or reinstall `pytube` package. Since pytube had issues in downloading the videos from YouTube in the past. - -* If this program shows not responding in windows, kindly don't close the application. Since this the sign of downloading the videos. - -* This program was tested on Windows machines and works completely fine. But it may or may not work on other operating system platforms like(Linux or MacOS, etc.,). - ---- diff --git a/PYTHON APPS/DownTube/downtube.py b/PYTHON APPS/DownTube/downtube.py deleted file mode 100644 index 51cb9c95..00000000 --- a/PYTHON APPS/DownTube/downtube.py +++ /dev/null @@ -1,101 +0,0 @@ -import tkinter as tk -from tkinter import ttk -from tkinter.constants import RAISED - -# Importing the user defined module. -import Functionality as fn - -def main(): - - # Root window - main_window = tk.Tk() - main_window.title("Downtube") # Title of the window - main_window.minsize(600, 400) # Default Window size - main_window.rowconfigure(0,weight=1) # Rowconfiguration of root window in order to expand widgets, when window is is resized. - main_window.columnconfigure(0, weight= 1) # Rowconfiguration of root window in order to expand widgets, when window is is resized. - main_window.rowconfigure(1,weight=1) # Rowconfiguration of root window in order to expand widgets, when window is is resized. ### - main_window.columnconfigure(1, weight= 1) # Rowconfiguration of root window in order to expand widgets, when window is is resized. ### - main_window.configure(bg= "white") # Background color for root window - -# Parent Frame widgets: - - # Input frame widget. - main_frame = tk.Frame(main_window, borderwidth= 2,bg= "white") - main_frame.grid(row= 0, column= 0, columnspan= 3, rowspan= 4) - - # Configuration of row and column of "main_frame" in order to expand widgets, when window is is resized. - main_frame.columnconfigure(0, weight= 1) - main_frame.columnconfigure(1, weight= 1) - main_frame.columnconfigure(2, weight= 1) - main_frame.rowconfigure(0, weight= 1) - main_frame.rowconfigure(1, weight= 1) - main_frame.rowconfigure(2, weight= 1) - main_frame.rowconfigure(3, weight= 1) - -# main_frame widgets - - # Display label indicating --> 'paste the youtube link'. - linke_label = tk.Label(main_frame, text= "Paste the YouTube link", width= 40, borderwidth= 1, anchor= "w", bg= "white") - linke_label.grid(row= 0, column= 1, sticky="WE", pady= 2) - - # Display label indicating --> 'Browse to save the file'. - linke_label = tk.Label(main_frame, text= "Browse to save the file", bg= "white", width= 40, anchor= "w") - linke_label.grid(row= 2, column= 1) - - # Display label indicating --> 'Choose the resolution'. - resolution_lb = tk.Label( - main_frame, text= "Choose the resolution", width= 15, - height= 1, anchor= "w", bg= "white" - ) - resolution_lb.grid(row=4, column= 1, pady=2, sticky= "we") - - - # Entry Widget --> Getting youtube link. - link = tk.StringVar() - get_link = tk.Entry(main_frame, textvariable= link, bg= "white") - get_link.grid(row= 1, column= 1, sticky= "wE", pady= 2) - - # Entry Widget --> Getting Directory to save file. - directory = tk.StringVar() - get_dir = tk.Entry(main_frame,textvariable= directory, bg= "white", fg= "grey") - get_dir.grid(row= 3, column= 1, sticky= "wE") - get_dir.insert(0, "Choose a folder") - - # Combo box Widget --> Shows the options(Resolution) available. - my_string_var = tk.StringVar() - resolution_box = ttk.Combobox( - main_frame, textvariable=my_string_var, - values=["360p", "720p"]) - resolution_box.grid(row=5, column= 1, pady=2, sticky= "we") - - # Button widget --> Clear the Input field of youtube entry widget - clear_bt = tk.Button( - main_frame, text= "Clear", - width= 10, height= 1, - bg= "grey", - command= lambda: fn.clear(link, get_link) - ) - clear_bt.grid(row= 1, column= 2, pady= 2) - - # Button widget --> Opens file explorer to save the file - temp_bt = tk.Button( - main_frame, text= "Browse", - relief= RAISED, width= 10, - height= 1, bg= "grey", - command= lambda :fn.browse_folder(get_dir) - ) - temp_bt.grid(row=3, column= 2, pady=2) - - # Button widget --> Downloads the video - download_file = tk.Button( - main_frame, text= "Download", - relief= RAISED, width= 10, - height= 1, bg= "grey", anchor= "center", - command= lambda :fn.download_bt(link.get(), get_link, resolution_box.get(), directory.get(), get_dir, resolution_box) - ) - download_file.grid(row=5, column= 2, pady=2) - - main_window.mainloop() - -if __name__ == '__main__': - main() diff --git a/PYTHON APPS/DownTube/requirements.txt b/PYTHON APPS/DownTube/requirements.txt deleted file mode 100644 index 0ff08030d864dc8c097638cae7a5ac3f610fde01..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 56 tcmezWuYjSFp@gB7A&DWC!4?P&8H^b8fTSLS0Rt}s7f>!6EN*}z3II)42&Di3 From 6bcbf34f7148bf1e6916fe3cdf2f81d5ad85fdff Mon Sep 17 00:00:00 2001 From: senthilnathan Date: Thu, 20 Oct 2022 20:48:46 +0530 Subject: [PATCH 3/6] Added DownTube --- PYTHON APPS/DownTube/Exceptions.py | 18 +++ PYTHON APPS/DownTube/Functionality.py | 138 ++++++++++++++++++ .../DownTube/Props/YT_icon_ico_64x64.ico | Bin 0 -> 11942 bytes .../DownTube/Props/YT_icon_png_64x64.png | Bin 0 -> 36544 bytes PYTHON APPS/DownTube/README.md | 35 +++++ PYTHON APPS/DownTube/downtube.py | 101 +++++++++++++ PYTHON APPS/DownTube/requirements.txt | Bin 0 -> 56 bytes 7 files changed, 292 insertions(+) create mode 100644 PYTHON APPS/DownTube/Exceptions.py create mode 100644 PYTHON APPS/DownTube/Functionality.py create mode 100644 PYTHON APPS/DownTube/Props/YT_icon_ico_64x64.ico create mode 100644 PYTHON APPS/DownTube/Props/YT_icon_png_64x64.png create mode 100644 PYTHON APPS/DownTube/README.md create mode 100644 PYTHON APPS/DownTube/downtube.py create mode 100644 PYTHON APPS/DownTube/requirements.txt diff --git a/PYTHON APPS/DownTube/Exceptions.py b/PYTHON APPS/DownTube/Exceptions.py new file mode 100644 index 00000000..aae18fbc --- /dev/null +++ b/PYTHON APPS/DownTube/Exceptions.py @@ -0,0 +1,18 @@ +class Downtube_Error(Exception): + """ This is the base exception class for all other user defined exception(classes) """ + pass + +class Link_Error(Downtube_Error): + """ When there is no Link is given as input """ + pass + +class InvalidLink(Downtube_Error): + """ When the ink URL isn't a youtube link """ + pass + +class DirectoryError(Downtube_Error): + """ when No directory is given """ + pass +class ResolutionError(Downtube_Error): + """ when no resolution is givenn """ + pass \ No newline at end of file diff --git a/PYTHON APPS/DownTube/Functionality.py b/PYTHON APPS/DownTube/Functionality.py new file mode 100644 index 00000000..868e27a6 --- /dev/null +++ b/PYTHON APPS/DownTube/Functionality.py @@ -0,0 +1,138 @@ +import re +from tkinter.constants import END +import downtube as dt +import Exceptions as ex +from tkinter import messagebox as msg +from tkinter import filedialog as fd +import urllib.request +import pytube as pt +from pytube import YouTube + + +def clear(link, get_link): + """ Clears the Entry box widget """ + download_link = link.get() + if download_link == "": + msg.showwarning( + title= "Warning", + message= "Warning: The input field is already empty", + ) + else : + get_link.delete(0,last= len(download_link)) + +def browse_folder(get_dir): + global dir_path + dir_path = fd.askdirectory() + get_dir.delete(0, END) + get_dir.configure(fg= "black") + get_dir.insert(0, dir_path) + if dir_path == "": + get_dir.configure(fg= "grey") + get_dir.insert(0, "Choose a folder") + +def check_connectivity(link): # Referred website: codespeedy (https://www.codespeedy.com/how-to-check-the-internet-connection-in-python/) + """ Checks for internet connectivity or valid youtube link """ + try: + urllib.request.urlopen(link) + except: + msg.showerror( + title= "Network Error", + message= "Connectivity issue found", + detail= "Check you internet connectivity \nor\n the link might not be correct" + ) + +def dwn(download_link, video_resolution, directory): + """ Downloads the video """ + yt = pt.YouTube(download_link) + try: + if video_resolution == "": + raise ex.ResolutionError + except ex.ResolutionError: + msg.showerror(title= "Downtube", + message= "No video resolution found" + ) + if video_resolution == "360p": + try: + video = yt.streams.filter(progressive= True, file_extension= "mp4", res= "360p", type= "video").first() + if video == None: + raise AttributeError + else: + return video + #video.download(output_path= directory) + except AttributeError: + msg.showinfo(title= "Downtube", + message= "The video resolution is not available to be downloaded ", + detail= "Try downloading with other resolution" + ) + elif video_resolution == "720p": + try: + video = yt.streams.filter(progressive= True, file_extension= "mp4", res= "720p", type= "video").first() + if video == None: + raise AttributeError + else: + return video + #video.download(output_path= directory) + except AttributeError: + msg.showinfo(title= "Downtube", + message= "The video resolution is not available to be downloaded ", + detail= "Try downloading with other resolution" + ) + +def clear_inputs(get_link, get_resolution): + input_of_get_link = get_link.get() + input_of_get_resolution = get_resolution.get() + get_link.delete(0,last= len(input_of_get_link)) + get_resolution.delete(0,last= len(input_of_get_resolution)) + + + +def download_bt(download_link, get_link, video_resolution, directory, get_dir, resolution_box ): + """ All the process for downloading will be done here """ + verify_YT = re.search(r"youtube.com", download_link) # Verifying youtube link or not + + + try: + """ Checks for youtube link and rasie error if link is not valid or not found """ + if download_link == "": + raise ex.Link_Error + elif verify_YT == None: + raise ex.InvalidLink + else: + check_connectivity(download_link) + get_link.delete(0,last= len(download_link)) + try: + if directory == "": + raise ex.DirectoryError + else: + vid = dwn(download_link, video_resolution, directory) + vid.download(directory) #type: ignore + clear_inputs(get_dir, resolution_box) + except ex.DirectoryError: + msg.showerror(title= "Directory error", + message= "Directory Error: No Directory is found", + detail= "Browse or enter the directory to save the file" + ) + except ex.Link_Error: + msg.showerror( + title= "Link Error", + message= "Youtube link Not Found", + detail= "You've not given any link in the \"Link section\"" + ) + except ex.InvalidLink: + msg.showerror( + title= "Invalid link", + message= "Error: Invalid link", + detail= "The given link is invalid or doesn't belongs to youtube" + ) + + try: + if directory == "Choose a folder" : + raise ex.DirectoryError + except ex.DirectoryError: + msg.showerror( + title = "Directory Error", + message="Error: No directory is given to save the file" + ) + +if __name__=='__main__': + dt.main() diff --git a/PYTHON APPS/DownTube/Props/YT_icon_ico_64x64.ico b/PYTHON APPS/DownTube/Props/YT_icon_ico_64x64.ico new file mode 100644 index 0000000000000000000000000000000000000000..7b5b87ab79acb5c92452645d980559151dba1eb7 GIT binary patch literal 11942 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 literal 0 HcmV?d00001 diff --git a/PYTHON APPS/DownTube/README.md b/PYTHON APPS/DownTube/README.md new file mode 100644 index 00000000..0902daf4 --- /dev/null +++ b/PYTHON APPS/DownTube/README.md @@ -0,0 +1,35 @@ +# DownTube + +## Introduction + +**DownTube** is a piece of GUI based program which downloads Youtube videos either in 360p or 720p resolution. Very simple User Interface. + +--- + +## Packages + +Refer [requirements.txt](requirements.txt) for python packages used to develop this program. + +--- + +## Features + +**Features of this application:** + +* Just paste the complete URL(***from search bar of the browser***) of the video you want to download. + +* You can store the downloaded video whereever you want(***just click browse button***) + +* Choose the available resolution(either 360p or 720p). + +--- + +## Information to users + +* If videos aren't downloaded, Try to update or reinstall `pytube` package. Since pytube had issues in downloading the videos from YouTube in the past. + +* If this program shows not responding in windows, kindly don't close the application. Since this the sign of downloading the videos. + +* This program was tested on Windows machines and works completely fine. But it may or may not work on other operating system platforms like(Linux or MacOS, etc.,). + +--- diff --git a/PYTHON APPS/DownTube/downtube.py b/PYTHON APPS/DownTube/downtube.py new file mode 100644 index 00000000..51cb9c95 --- /dev/null +++ b/PYTHON APPS/DownTube/downtube.py @@ -0,0 +1,101 @@ +import tkinter as tk +from tkinter import ttk +from tkinter.constants import RAISED + +# Importing the user defined module. +import Functionality as fn + +def main(): + + # Root window + main_window = tk.Tk() + main_window.title("Downtube") # Title of the window + main_window.minsize(600, 400) # Default Window size + main_window.rowconfigure(0,weight=1) # Rowconfiguration of root window in order to expand widgets, when window is is resized. + main_window.columnconfigure(0, weight= 1) # Rowconfiguration of root window in order to expand widgets, when window is is resized. + main_window.rowconfigure(1,weight=1) # Rowconfiguration of root window in order to expand widgets, when window is is resized. ### + main_window.columnconfigure(1, weight= 1) # Rowconfiguration of root window in order to expand widgets, when window is is resized. ### + main_window.configure(bg= "white") # Background color for root window + +# Parent Frame widgets: + + # Input frame widget. + main_frame = tk.Frame(main_window, borderwidth= 2,bg= "white") + main_frame.grid(row= 0, column= 0, columnspan= 3, rowspan= 4) + + # Configuration of row and column of "main_frame" in order to expand widgets, when window is is resized. + main_frame.columnconfigure(0, weight= 1) + main_frame.columnconfigure(1, weight= 1) + main_frame.columnconfigure(2, weight= 1) + main_frame.rowconfigure(0, weight= 1) + main_frame.rowconfigure(1, weight= 1) + main_frame.rowconfigure(2, weight= 1) + main_frame.rowconfigure(3, weight= 1) + +# main_frame widgets + + # Display label indicating --> 'paste the youtube link'. + linke_label = tk.Label(main_frame, text= "Paste the YouTube link", width= 40, borderwidth= 1, anchor= "w", bg= "white") + linke_label.grid(row= 0, column= 1, sticky="WE", pady= 2) + + # Display label indicating --> 'Browse to save the file'. + linke_label = tk.Label(main_frame, text= "Browse to save the file", bg= "white", width= 40, anchor= "w") + linke_label.grid(row= 2, column= 1) + + # Display label indicating --> 'Choose the resolution'. + resolution_lb = tk.Label( + main_frame, text= "Choose the resolution", width= 15, + height= 1, anchor= "w", bg= "white" + ) + resolution_lb.grid(row=4, column= 1, pady=2, sticky= "we") + + + # Entry Widget --> Getting youtube link. + link = tk.StringVar() + get_link = tk.Entry(main_frame, textvariable= link, bg= "white") + get_link.grid(row= 1, column= 1, sticky= "wE", pady= 2) + + # Entry Widget --> Getting Directory to save file. + directory = tk.StringVar() + get_dir = tk.Entry(main_frame,textvariable= directory, bg= "white", fg= "grey") + get_dir.grid(row= 3, column= 1, sticky= "wE") + get_dir.insert(0, "Choose a folder") + + # Combo box Widget --> Shows the options(Resolution) available. + my_string_var = tk.StringVar() + resolution_box = ttk.Combobox( + main_frame, textvariable=my_string_var, + values=["360p", "720p"]) + resolution_box.grid(row=5, column= 1, pady=2, sticky= "we") + + # Button widget --> Clear the Input field of youtube entry widget + clear_bt = tk.Button( + main_frame, text= "Clear", + width= 10, height= 1, + bg= "grey", + command= lambda: fn.clear(link, get_link) + ) + clear_bt.grid(row= 1, column= 2, pady= 2) + + # Button widget --> Opens file explorer to save the file + temp_bt = tk.Button( + main_frame, text= "Browse", + relief= RAISED, width= 10, + height= 1, bg= "grey", + command= lambda :fn.browse_folder(get_dir) + ) + temp_bt.grid(row=3, column= 2, pady=2) + + # Button widget --> Downloads the video + download_file = tk.Button( + main_frame, text= "Download", + relief= RAISED, width= 10, + height= 1, bg= "grey", anchor= "center", + command= lambda :fn.download_bt(link.get(), get_link, resolution_box.get(), directory.get(), get_dir, resolution_box) + ) + download_file.grid(row=5, column= 2, pady=2) + + main_window.mainloop() + +if __name__ == '__main__': + main() diff --git a/PYTHON APPS/DownTube/requirements.txt b/PYTHON APPS/DownTube/requirements.txt new file mode 100644 index 0000000000000000000000000000000000000000..0ff08030d864dc8c097638cae7a5ac3f610fde01 GIT binary patch literal 56 tcmezWuYjSFp@gB7A&DWC!4?P&8H^b8fTSLS0Rt}s7f>!6EN*}z3II)42&Di3 literal 0 HcmV?d00001 From b2e2731001855e1aa1b2ffad18bd5e663d56ea67 Mon Sep 17 00:00:00 2001 From: senthilnathan Date: Thu, 20 Oct 2022 20:53:49 +0530 Subject: [PATCH 4/6] removed downtube --- PYTHON APPS/DownTube/Exceptions.py | 18 --- PYTHON APPS/DownTube/Functionality.py | 138 ------------------ .../DownTube/Props/YT_icon_ico_64x64.ico | Bin 11942 -> 0 bytes .../DownTube/Props/YT_icon_png_64x64.png | Bin 36544 -> 0 bytes PYTHON APPS/DownTube/README.md | 35 ----- PYTHON APPS/DownTube/downtube.py | 101 ------------- PYTHON APPS/DownTube/requirements.txt | Bin 56 -> 0 bytes 7 files changed, 292 deletions(-) delete mode 100644 PYTHON APPS/DownTube/Exceptions.py delete mode 100644 PYTHON APPS/DownTube/Functionality.py delete mode 100644 PYTHON APPS/DownTube/Props/YT_icon_ico_64x64.ico delete mode 100644 PYTHON APPS/DownTube/Props/YT_icon_png_64x64.png delete mode 100644 PYTHON APPS/DownTube/README.md delete mode 100644 PYTHON APPS/DownTube/downtube.py delete mode 100644 PYTHON APPS/DownTube/requirements.txt diff --git a/PYTHON APPS/DownTube/Exceptions.py b/PYTHON APPS/DownTube/Exceptions.py deleted file mode 100644 index aae18fbc..00000000 --- a/PYTHON APPS/DownTube/Exceptions.py +++ /dev/null @@ -1,18 +0,0 @@ -class Downtube_Error(Exception): - """ This is the base exception class for all other user defined exception(classes) """ - pass - -class Link_Error(Downtube_Error): - """ When there is no Link is given as input """ - pass - -class InvalidLink(Downtube_Error): - """ When the ink URL isn't a youtube link """ - pass - -class DirectoryError(Downtube_Error): - """ when No directory is given """ - pass -class ResolutionError(Downtube_Error): - """ when no resolution is givenn """ - pass \ No newline at end of file diff --git a/PYTHON APPS/DownTube/Functionality.py b/PYTHON APPS/DownTube/Functionality.py deleted file mode 100644 index 868e27a6..00000000 --- a/PYTHON APPS/DownTube/Functionality.py +++ /dev/null @@ -1,138 +0,0 @@ -import re -from tkinter.constants import END -import downtube as dt -import Exceptions as ex -from tkinter import messagebox as msg -from tkinter import filedialog as fd -import urllib.request -import pytube as pt -from pytube import YouTube - - -def clear(link, get_link): - """ Clears the Entry box widget """ - download_link = link.get() - if download_link == "": - msg.showwarning( - title= "Warning", - message= "Warning: The input field is already empty", - ) - else : - get_link.delete(0,last= len(download_link)) - -def browse_folder(get_dir): - global dir_path - dir_path = fd.askdirectory() - get_dir.delete(0, END) - get_dir.configure(fg= "black") - get_dir.insert(0, dir_path) - if dir_path == "": - get_dir.configure(fg= "grey") - get_dir.insert(0, "Choose a folder") - -def check_connectivity(link): # Referred website: codespeedy (https://www.codespeedy.com/how-to-check-the-internet-connection-in-python/) - """ Checks for internet connectivity or valid youtube link """ - try: - urllib.request.urlopen(link) - except: - msg.showerror( - title= "Network Error", - message= "Connectivity issue found", - detail= "Check you internet connectivity \nor\n the link might not be correct" - ) - -def dwn(download_link, video_resolution, directory): - """ Downloads the video """ - yt = pt.YouTube(download_link) - try: - if video_resolution == "": - raise ex.ResolutionError - except ex.ResolutionError: - msg.showerror(title= "Downtube", - message= "No video resolution found" - ) - if video_resolution == "360p": - try: - video = yt.streams.filter(progressive= True, file_extension= "mp4", res= "360p", type= "video").first() - if video == None: - raise AttributeError - else: - return video - #video.download(output_path= directory) - except AttributeError: - msg.showinfo(title= "Downtube", - message= "The video resolution is not available to be downloaded ", - detail= "Try downloading with other resolution" - ) - elif video_resolution == "720p": - try: - video = yt.streams.filter(progressive= True, file_extension= "mp4", res= "720p", type= "video").first() - if video == None: - raise AttributeError - else: - return video - #video.download(output_path= directory) - except AttributeError: - msg.showinfo(title= "Downtube", - message= "The video resolution is not available to be downloaded ", - detail= "Try downloading with other resolution" - ) - -def clear_inputs(get_link, get_resolution): - input_of_get_link = get_link.get() - input_of_get_resolution = get_resolution.get() - get_link.delete(0,last= len(input_of_get_link)) - get_resolution.delete(0,last= len(input_of_get_resolution)) - - - -def download_bt(download_link, get_link, video_resolution, directory, get_dir, resolution_box ): - """ All the process for downloading will be done here """ - verify_YT = re.search(r"youtube.com", download_link) # Verifying youtube link or not - - - try: - """ Checks for youtube link and rasie error if link is not valid or not found """ - if download_link == "": - raise ex.Link_Error - elif verify_YT == None: - raise ex.InvalidLink - else: - check_connectivity(download_link) - get_link.delete(0,last= len(download_link)) - try: - if directory == "": - raise ex.DirectoryError - else: - vid = dwn(download_link, video_resolution, directory) - vid.download(directory) #type: ignore - clear_inputs(get_dir, resolution_box) - except ex.DirectoryError: - msg.showerror(title= "Directory error", - message= "Directory Error: No Directory is found", - detail= "Browse or enter the directory to save the file" - ) - except ex.Link_Error: - msg.showerror( - title= "Link Error", - message= "Youtube link Not Found", - detail= "You've not given any link in the \"Link section\"" - ) - except ex.InvalidLink: - msg.showerror( - title= "Invalid link", - message= "Error: Invalid link", - detail= "The given link is invalid or doesn't belongs to youtube" - ) - - try: - if directory == "Choose a folder" : - raise ex.DirectoryError - except ex.DirectoryError: - msg.showerror( - title = "Directory Error", - message="Error: No directory is given to save the file" - ) - -if __name__=='__main__': - dt.main() diff --git a/PYTHON APPS/DownTube/Props/YT_icon_ico_64x64.ico b/PYTHON APPS/DownTube/Props/YT_icon_ico_64x64.ico deleted file mode 100644 index 7b5b87ab79acb5c92452645d980559151dba1eb7..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 11942 zcmeI2u~Ne@42G3s$k3q!cg&0|^cCn6@Bn?9J_2ib8Ttgw9kZ}=#{d(q9mS2?SV~}l z{IYwpY)QS#_bX|eT!||GTHlkOZ|P}8bVEdU-qzkW-a@<`zUF$t#sQ+Y-AXg(^K&||ukQgMWvv5$7*;i4`hK>iLr>nXENReF4_6j+7*d}{&^0)xm3OE*$i%}8 zq6R}sJ#yGRLVZ{2v~m{ZT#yOJGYA@tQ|2oW*aO3F1`7L6`H{FI%as0qHZM@0`H_7} z|I=E;wbcKQ*Js$Cr+v)vr+M^s%8$;PBk^R7sOD4T>a{hUGYBDUw9PHYB8hBx%~` zXi`dsRHD+LP&861{jPghwSAtC|LZ*8=f7UBbF|mGhwHwE_ch%2+WWYn!IGiB$^6DJ z%+O^^7p`TP!Bz|-c5;vy+&OY2Fcbb6ynE@!y$mBh3jHU-Tnv_moAO(iu3g12?td^0 zdx&8=df?wWhFLp^5&7MOVde%hjFe-Mshl6foL;XKma|gH zreV*vY_~AvP5a<7_g%qBY^($31a=2WQmD(>3iVQw)2cQ!`^MKkzp9u z9t-Dx)L}V#!>vS^#swbFV5Eu`vscIS;O4;*aI-b=!eHp-QD(4U8!&3jAA)|&iRE=L z3p0Az_?UgifNe?xL?oGlY5kPoChL^@y=}rl;G6jbK|i*6_(B+x5oq3ZNm)5ujPYC7 zPnpRU+cO$&GUql)Mvpil09L~Ef**sz(M|S>%*(db@1z(%J7}6Sf6f4O*KbvKve&e) z{g{Ua>;P7x0^MWIEsBXz7f)vRfAu$EMt?Xo9p4?YRwd@p$s|VpD%{=H@n{mh8~cf0 zR5ieiDNuk>-J2#1!*{dK-z)lYcoWko3T^dZx#-e);+1AR++M_RZa-z#$$L;ge7Ej; zw}i0}o6~PP^K}(Fpx}~bEoofs%M)ikM_Gwn^FB0`G)&a_ z*r~8blD7=z`OAnjlMS9D&cD{*gw;$YHwrdYDzp8d-}VEd;^fxGiTVAqwL~NhUus?@ zcl`ECbn30VFu-p@KV|u+j>=@w{8N8zWX4Ps^lNV3fs_U=*S{=#z6{tb$@2~Auw3o+ zC2VjkJ8$GB(w_H4x9)(}8cAN@Ah?@$hb)}=zWUsNfmUjt(h*6FxFIRY~Pze!I&)A893GQ>H^3dR&^mk!J(*rle}ohQ$(V zBn^M-r(Chw(SyvuJj{y{KlslGtAm=L%lJApF}t7BYIZ=4XBF+{r|z(6Q1xj>Jf|%a zDKA{|j`olHsRH9OysGGYhO_@vmS5N3#PGJJBx%HKSIF)swwxW%;oU;-2sBf>LvBfn z*8y9rzYL`lFbB43OBU?yr=0zCEFFy*w%Biga|k1Tw|fhbH^ck#zVHSV6TTsKlga*+qZNql|HY#98G?Qb%=A%nZMG|NbN z={$DP2!k$qi}!8N9V@w8i+TC0zjC)6?>+j~(s;;TFws|WKfTHKPVF{Ni85&%r(t!b zXcQ;@0`L1<{@!2AF0wEvYi2YuoWm)@+2;yK95UXJ(tvc9rTQ%t?!|A#^h4`N%D2-1;fI zyY-`5$<&EWkPP}maK%_rmv&ZcqG9vNS(&YhtrSB|iH)-Xzz-s0&4JwV}G zotxayt(SHjJP7nG1fPbl>X2O~LryU#9fAe)4s(~6#Va2iyZbq}h!g1=%HNl>8S@M% z05i~B(E zJBeGG-+q}v50|rtM^U6;3^nd=ex}|}`K!}E9Mwt_&HE3WEcj;`N41igXa9i{1^+ze zgrTIJFliF7UPAjOw~TW>JN^V;@@^qFd*jg3*kUOW$#UI;TpRvXE8qSmb8NWJIu}C! zl$!+Oa(yb^IsdM679t0FjgJ~PUwQm|_OL(20!C0)r`$eXG!n{@dXX;>)^NMQREGd^ul|olfuV(#3G&c<+sY-AlzF9-)`K{z8*63WDZzZVGwJdy^ofX#T7BhWG40 z#z0s|JFp%jH*aT|Tz}36*skY+LoW1bnfxCjMlLrY5GPl$c*tW$Xuh6>CZcF~`k3KM z&sG$b2L|-sm~&6jb8_E&TSjbdJHzB2lH(dIUDl^38Cypfh7F3z%XgufhQ^x*2x^Ska%h7;5H^zQoVx1EU5UPB zp;e@Wi*bGBeVtVfOzN9?{KCKD2LjzZuDdYZjWf>=?`A~K4;;@u)j1*gencNzm??u} zAFgiV_|1E^a|>WluciB)Wg?T#d2yXb2WY$(>FX@8I3An2fUWtvxBWFC!oEJba>Wr$ zmh*HBnu{BW?Y&*X}7jtz0UcS_lXHv^?X z>btqt!crH^3vdYnsf%*({!>@y1ycfLpsV43>&i;-?r!OU5y+WuD>f=jY!avH(h3JAMd_Phu!9f(SXY`dKln2ub9>3n+`8)Tq z)$()FxIVgg%YQZmA(9TA`{X4|(%KSHL(*;hQy#SG&`o>vL<0mfk zO>uLj$ax`#c~xKec1NU-dLdl$+YnU#=Z1aq9QT;#ziPNWD7DkPu$Sxqv5phth1by$ zSTZd8zw_fK8`jCL-Zdyvj3oX*g5T+(y$)NrA+vU>EnD;T5J#nwT(Qe;vn2s*k*1H5-}>>%#qHB{oThy`KMwJ1wyFhx3)}p znT%LwU)!JbuWV?&f%}F9z46A+`X{VlLf|@ar$nV@luRGj=Kf06gh(2_D5*ScbD>ID)TtnA$sTzy_#RPt9L;Kyn|EL!&AFyG+V z*TZ@t!cSdjvPCZg!&m=|5gc92l$r?wTkVzHXP#uqf5!u*TcPEiP}|@d`mKE>di(x@ zUO{dP=p*!Jy%y4hMXx^BNW`3PQ|UE_-G)MR3VX(%^j#=SkPp8tX0q#OubZel8``$? z-;A+V66k%@3n0}8`{?D3Y27yK+^-nBQ5+cCn>_eQ{fo)YElMiYHxy>pc_dZNvY2Wi zss55#!raKY_BKz>XnLh3sw3lzFZU~O5N4JT4Dyz590UvLH4?uiLJQgcB0hZ0uQa63 zXqdG!&TOGeug7DPg&^W*b?h6Af9mzycBuMslna|KGi9i%z;>)Y_sq{)h$vwO{8}C6 z4KUetm5a?7v3kKc|31n7E9U+*_H%iV6jxVdm_=6q9as5JhB+_M|FsY17Q{?x(}o3V zai0xG|8Ey4Btk=%hI>7m)g`=_a>KF8|3UyiGhA4jAC1{U4O?z7{?1>iRPzk)U&&8+ zI3Y{F_R^P$-1USTnm6JXu<|RV`6bOR@!vY-*2jGv*lWqz;=l4|!hS~x#33;IuYtnP ze$#!7RlE;ZxMGx4|Cf_A`h4~By7Q}vQhtkly||Z%(INjAjs17483C*5^$tp+zgSII zif;J;Vc_~#gA04ueMsG>aB?@4{6DDs&+-tSL|ExQ?f@M&;3nj+{DlrTdLDSF!2gvz z{@wL9*SRt+pZ0V;S^nnsI8|JdXNO3uA|uK^ z`r)Sg%GHHc1;tB)d+Oc0etbW68IMAXB2L#!mQIvemaLN`>G(uBy>^y^5`GL-5a`dI zO7iypbg(D6;~&?B+f^lF5-NRGYi^C3Yfs|bw}q#VgXD<3oI&GlydymsDh0{-GWVKx!j%|f6hn2u@Pn1vR zf$#fXtZTavdMm3g+t9;f`yU1tk&BYn&f+1`*zuT;yv$v@;=i?hy5uwI%9-@sy-}{6 ztsZ8t$7n7jjc|s=n+N<^xH9u(!mFx6@%@z!yIzu9_aSUeK)W&f_qTX`>~YcjXL)5) zmg%g$o2Qgnj#a~L*coJ=!Lt#IrB7I@?8=OcTd^nVqBM5YJSm)q%2(Y_C^hfUq69bY zp03x?Y!qyEtT=l8P`vUAW|#~ZC<+(dKb5_5G4{3q7R^4}o|S0Swb?;0f0xf#RYWvz zIW{QXGD39P?J}QHwMCT%QB)?KSZP2e32&1Iyi6KlG*VwrRdTOXVCB=utt1PHC3TbR z2Mr-EPrZG2C>&omYC+SR=R1Ngn4bu@-O`BWVGfYO&BTdY+;5Es!llPYXM{&;R#k8L z!)5A*Ls;Eip}GxCx4p?UN{UId#1y-_MgOj7U0YT8^#RFc8cC%!`+>`WnQ4yDCVks% zhp4PbsaI7FyVC#0leG_{1LZT`U)~Oyc05bYF5}6PqRQ@969!^kuh>LI|IJ0L$9kv6 z**SBYUby~vy;$~=`RTFokA@9F1Mx7sS>21E7Om@b7cV(dSt&KG$gGS(m=9 zNu}2goho1khDH0(KznBV-sak1LisQspY+7sqACf^#j-l65x)_ypJ$BZu5i3O*uqD1 zhh)|A?Qe)8{zDc1t*`Bjo;NBfu9I#R+L~=N^(psE{ry|yj0I>ACZj4x5&kJq%Ue8h z#4;V-q7(X|8;?nR`Qr%orlVmy5FV>KXh)2VP2HuL?yl;Ua(_hD)2PLfM*DFh&d#UA zHmsj|xcz|h{mtDwlgGz_ex(%78i?Ixwk{TCfiX-HG5JVkiQU*eTU@lx+Z`Q>vxn^I zRM;-zrVr9yfSj&Je4I5`b+lBP$#H+nM7(v+RQpF>0$KzDFfCYcpmkBDPtf6ZolH8& z?_>}&?Wu=*v^9u)0>TSjTUw%uZ#~?G`Cok~5|xqt_Gk}C9`NoE8fj8ftJ$=&?oacB zQ^Mx7!$XEAc~4>d5APf!iAE$fR!^%fW3kPTXrR8Q$};p+&zGp z>Z5du_>-%8b7gB)EPcz%rDtJ>!;c^Xydxq-I$UHeS!K)Va3~?|aNF~fORktkWm=z1 zsEUfzzGHrK3}#=!2FyNw!Q9eVFO8xT-eH$~eec_oobI9GDu@F#V2SdM;65f!t}6Nu zKxaisjY7$XLm10^mCS+rsc?474!68w* zPv3@I&ZUA}uvKBt*B76fKIc%+*-6U(%cH>NU#1)C9O$+PzK|#>9*|B((Iu7J@S>6< z8={P=n!BQ0?ew-ptW_u8U?3KQ{J_s;r5bHoU$-8o;g@SOjm_o20bugG6V8#)4#JWyT(l3scTY(p*AJ@z@}& z_0|7;H7Fgf$|o!Ca@;x%|^*7!|$xSDT9&w97(_B z4+AYbCpAfMzWUBk?D=!ZLbP^&E4x2o+j{Ipx?0+_n=&KD-q|=#1M8R4nQkZzC_skG zitOxM8oC{=8U-2O@@Hh$XA&D@&&J|P6@LX6WOUVp%=z&y?}54YHf#ijyQw0K6OVP; zO1$6FVLq9b`ah=rsB(FWCCC?0J-D<<`-mUHQw(+CK66~$0qPu93_=`UL_)Z_Gx-{~ z-@Uv1puNLqPNI%InPw)Qh8>Tb$KsJ`S}8IyPn9zZa~@#k^X8GzVc`9@2V;j@xHm0b zA6fW7$6LGXqGOgpII&khvXhs6@IdLV1xm+YmawPsl?^Xi_Sj@;U>B9GhJ_lycJ-<;NdDgRw)nOWi3(fEMu2=4!ZFonC%*l-FCDjEg7}EmGZ;jBW8kNj zDWkIT%TB~oqvX-O!QZ&JOkYO^yWb5X@cgZC{tH&iw*vJxST!Ugu1KS@z23wV7g8O^P6{YAGo;Y?%* zruDZfufjhjBHEVb7OKNP*#{eTC(U_i2P(P6Df;&>5Te9=8;KDOo#=uVexlj|Cngk( zN}BM>zp1MsX;0Txqc>4L_>?76e~56P8rVD8Yqq4fR+_Zj8!cjWv9QO4rkc1#Gkxyd zru91dY2&lLASoMPsjJ{BQKR-ys)&){d2{rV zMq6$eMFtp10;FN~#_w#buN>y($bTj(&;liG5yb2cB*!8j3nX|G1VtdrFDH=j4$h z`B!i~6SqM8=A>v~_WIa?LIQt$vfT>|T%+D!)f!p7;;5+hmZRdc5`u_&hGVzD%I-9t zdW5&5;fTrV%)JoEbiJ{?XS)xtkv|(tfsr)}KDy4*Ub=C8PV+iVbMyYTrmx)M^w&YFpfMWR=dNnB#GxQSkRq^Sc_n2) zyxR6<@TEAJ7mMmPsH3JP&||PPsZtL z+)z8$2a;0yX(JQ^N$}6Y=_foeicX&`t<;P#u8I$`YucVcv{#G-z&wW(h;45eMK*m1 zOsH%)d@N*nA$GfUktu+}TajkSM}k;>Y<8Jmrjs%}u$15vubgaW@+MH32G~V5y_YJz zvmuJakh+Qj)R{jMY;WROSNG`Od-SyB727H z7)50M+s(M4pODqOI4ZtphZ>RALpULfMz~B2CUSpOdxz2OqQy8yVxtK^4PB{&{0Wns z4`W$6RsQInzJL|Ar4j{X>ZyH_XASsi&xJ(qLk;#+FRG8|& z$L)tpvU>SxPN;Bx3%Rc9^jBX`PvC_ix8o80yJJ|W?+R*djoFD92|Jm5w2;r$!;Iu`Eir9fD?(^Rp)&A&>M{wy~5sJ6`p{ytUa9i;W z;*WX9iO{XWK%vHa-~Rnei21P+AF}M_#A7j2uvp{lF72s*)9_$jBzD%k$HW&@U?85Y zCnFs9@E4L*7Yq=Jd+tCg5lP}6B{(aJS8b5AYmncbK5-Iv^XkTBw3yK(r*hiTQg(g< z&j6yh;EPaan!YRj&WqeTX4(V|2B7otpc~`sz@LvFe?mO0{FwKq@i;7I`w^4lNg&2n z{_M{UNz%jA-68%wFuLs2e#yvZ(d&mGf6iyTl%NoTPi8wW!Aa?yxwHzn8OJ-kGFsU8 zeC4rO=ZfkWYDshJdiz@$aPyf4o(l)=^B`s`1-siY1lTx zJVx~z7%bH8n#xKtF1(#Xcs7QhJ3HhxfbTJD2$PBv3wtF?517ecMc6;v!na4qd-{)- zd=eyNOVe4+uyYH`+}rZ0DJLl4f%&K{2;H_3gJztpNcJ#iw#GQOW zB<->Py!du8MxmL8*oL!@=XGiyVHP5b`*`)yp0EBjuHW7hwHR`YK*oHp0UpMn(kg)( zRmJtjcg(1J@T7~A1xuEJ%3a4(hx9KoB*dguRuC=%P@4{m@e2~;N%%hoqfDvoEkF>)ubmL zkEk`>DuD4o1|y#5_-s|9Qe7%x$>s#*IeyA^^Z2)F&&H3 z4wgid3?o6a?@31&Z~gku+T?`E|Dd(B)8k2vO95n11{&B~T*O6wE|_Gz3_(kJV_6C= ze_=@?pMf;M6}r^ zpK^)T~83evMVVYgW{5g+9`zwS^93ZHmWpg@WbXBL^q&RQM?12LtSlE zF_robNqyynCEJJN2XzU23~n8HM~Tq!0mH(nQjrb@tDdMTuf(E zNga|o{HzznvNp9e6yIErExK+B;kuy`3g*JhPnL&-`mQcrf^np741KsX3FIY1ykUL% z{apwmcoGx~{y{|J+Cj3XyWWh3Q#hQU^-ZYtW8M248SmO12i5FXWaQuI$iGqD`L*;%EAc@SG`npImPfDVZ8vH@lZT-Q2+e$BPLtNkDyb9$bWOesq|E1 z;~*}&@^Di;?ddwrh$c85WSVlo5Sica7LO_92FuuXzk<1C}_eE6!YMNC< zPKGpyW<4REnhVa`S`SKp&PllNYPf}1Bs-c`SX5#FIpd19hLaL8ek~BG<`h`A&*>o< zC9BU@y}fi5FX|8aHpU!&Ov2`$gE;hZ6zay#pg~+qU`*B*N0JVoV+k|(CxIEKXZ*Cw zIGP?^=jWzEHTmR8%iHOXW6Vx~>9ALtZ{kDe{3WE+;Ajw&d0awr>*cv*DWn7mapQpx z847El5bX~;d<}LefksxsmnRCqcoqQ6`l6_Qp!lBfEf^_BlO0`#?@Q|x{wj_c+TxtE z=fPVAXgSRH`ckk2?YQA#hp`d(8B^fs+`Qm7-ZM~G&-D>eXSi#t z1sML*_ohv+K6JEiBQ7(0)L1eusx(7^BU(8s<5HMohEY6@H_ga=82Kusr%=ul-csXUJIFr%cKD~quJ2#D2S11%KQ;Dt%dJ*Q~8 z;rOIbyD6un^w}*Ek$fjnHDk!ooTL5SvkQr_qrb$EM6;C>EYBrj^Uw@6h$!me*uX8a z4O9m&^`YT;e}m-4!CpY($rVkt8!(&r#+0rBuphC3UV-9$Wib-F?LmO;!l#O!dNc6|s-s6(L}>I}2|3Jpty z^4HxwD&DgoUX;Jm4Q-T%aA$t_e_(bOV11P z_@GWx_19}~kmO4CO<*6U1=d*-@7i|g@B>?H1RVCUM{P+pdUaaz?~c>gUoj{9A*mGH z1PUmqd{l3mtQR-j@-J*(1@ui9tJR=5GoF^8NO-RRJNBq_!*8YXso#hU3$|1f)Wn;D zaJO?bS7JR;@^9bd()HPvloXe*ApGG?MgH!RUB)G~mOY8YT5BktRq#+fWpY(cd|`V- zKu9r;>DiH#1G}y8PR!3+6FG$KTAMEoV%!kEMB?4s2I}v6L%f0wc`Rw^iP0jecSfZIitglM)KU ziYDwI`sqt;WN=ziK-MbX2w;DmMA7CMEV}}K@X>M4^=IXns}eFRF1AGEDf=Lh|)-2 z9&zo=oVBQD)ze!Z&Nfp66U@+lDPNCjLhE0S(VM47)z;^=*=zWYMNT7>l*kyqb-nqc z?{PhbHC4Imrf8buehGBOIxQH~D>eCyn&zwLHi>vbM^b2D3jDuDZ|<&-h-iKsaP!oD zGT}E$O`T1-*(1}E?2=C#UTspuOMFU=te^$;Tdl7ZusUXywY_IHGTc^j#)M(woAw=R ztL^BpRkV(d+7`9%IeIEfV=*TFF0ea7L9r#KC}QvCJS>aT@RBlOH*5O6P~~V#f_~@f zj*Ra3WT)*~0d^<|kR>}EFm{`-x2I}Pdy%4=s`JD!^RUO(b`glfg3EMGh6gMyAAOJP zs{GqD-^?Os`KNPhV1PNJ3F0u{|0z?y{qBXL>emc=J@em@P|YFtf{hC z@!{e3&JqKQ#5rLDijW6#6{E}%D+A_i&9cZ2&=&95-Shx)&UGq%*nY@6=}3H#TGiel zN36F33F;LE30q<#x@w3QroBPy%rT^)ZXNN48|WMEbTY6=xvz_W**J!{H>Y<6f4MWhbld_w)96gv}d?bM4! zGgfZDJ8KIFf)$|>WIbCWd*+^<{X^AK&m^aGolm_6W>1|pM_2nwok+= z)jEy1ZAR7)2Hszq>lT@EDP-{r#5vE!1mKy0JHN=h@*ZQE*Kz0Oo4trZ{1}eb;fUk5 z?plsz`|$%y?tveA@N|2CX4dP(xK9Wmd2cx}`E`E`e6;L`s=wN_j)rP~9h-Z|;0@PN zI_LDWTw?O5BP@5SqosZA*VEW2_tC7ARY0t(jf-@vDs#Di39sTM_b%e;;smfM?Xvtl zMe=}F+!*(C&^5~^QSaMg##<+Sn575g#I)p-uQPDZhKFg&k;%sQ_iimk7{OZ5~59=-}JeUc@x&a_>~>IsnJ?0e0UsaEM)? zK()j~r*cFGI$Fu2l@@v{VAk z=jL@M`AnSVtGn-zoCKFSV!}ZC+v37Pa}B=>I}yn{Lz@ZdY9xSJMEbv zjvt7U)2;BmGjYI+tkdyM4WH-hxw^gqL=f<(*JI2}LHKF;4)5#R?cZN~ghV4oHZ1|d zXOyQN+1DL;)Xn?d1;BLnAh)ou8E9c4Z=Xa~#Rcs_a3Rf5d0LfwbjrNSJ0Q{WIT`VZ z=S}sD>HG}pVgOJpr4n6Te>i9*_Gqn~gWqlSqtm-{*~Ppa4hG8a-)ebJ2U=R?zBnTj zg>b8_!Oo;MbBAi-ZZuz=9G$Pi5W!RPkE_YLFP=Eapas}+Z!yVUgq8QyR4%uWyOiIZ zc?_+=lq2UTvh0w7E8OgEoq2l%ld4N?iB-4*{6{$XY3sV6WB6G&$7qE%VQj{%R;NKS zbNUV9i!i?4;G9;08i0(adVH{^T>XxSXR|@x1uw8N7>?3z3iM?JR>(%fvW#2aXSjT> z|B()I;yuN3;;pLJkXa0p$teQZp9)~BrJq=Zthm5Gc^#}6F85c+i}iF%>F4$|wXDlT zx6X>AAP5aJHD_wk=NZ+9j%N8Fu99PnsQZb#IUV8+Z*q^#0^WpjHsGF9w@@+c?WR0O z2m9(h>pZ{^S~VyF6{ta<+JyJboi+Z;URZ90@l&2^llLh2ileI{wGM2a+HfS&80KC5 zde!sxZ#`?M`;onQ<+f_VgY#+$33nS5;5|h4A`MHJCD)ZAPkGL|wAy&}A9={bvafp| zRc4%==2r91${m*;p=H_9bcK<<1;eEn*3`dks6Pg82eTJ@WecNqeTx5JW+Jy-X4N6+ z6i>q#W>`i{mQnL1@i@0)8C5Z0fhK2>2Z2ezTDR^W4lWsp`Y?`yfiRqhhuhSe_q)#? zLA0fE0{{9J7*%Ag-BEi|e538qv2c2`ZWCt-mcMTgNNT7_o>jQiG-4>gQ!9Lf2G9aK zn2x~1y4rP#--`>ULBKGRQ?6Q#xz8R4eDLW?tGRl+BOB(jVtb#hJ-5MO)r|KcB~oW! zEklbE$u} zXs=uNeD3DqFcqKE{|KW~G4J0TzNxeGfjh|Fk0T+%@Z5&$Qm>poe>Mt59A{EFMDQ|P zW7JV(_aZS?@7!!tF_@@b5-;}U!oB;m27xQ;jCgv!<#|Sj^8F*H;Bn(Ps{B6nG9t{J z*^4&TI2@SbxKiE!l?_^(hXmzS;}YQ2f$iFw_m|*)ocHCO-R6A7zxi*|A05rhHB2jk zk8A`-4zMcvt$Fr9317viQHUB>?iFh3-7r)9)7L@vcxioJfe+KXsWi=hQ#iH^I9+0z!#Tm+X7gRtm_!52^g9bk{Q zep0?U6RG~+2zqwhKrbobVxZ$~>$oYYz_&S^6mFcka(ivr#k{QCh_ml!;q}a->v@@u z3^b>BCAcG`%ylE^cy)V#g2~X`!u}2ch`Y*Y3QL#_b?gVouDC2 zopv300Ri^+(!r*g6uRZW=z>j~rRoOyo;R3GRHSY*H_-%?2?U9&1=$Q6D+)2P80A5M zD2T55$#RRZH+j+RfZ%)$`eb)HJUJS+x+bUU-L+>k2LXEcP?lC4J7wU!R?j4NMPOju zE2lQp?KIb|KXfb9ZW&{)bLO6b44?=9I`=61L{njK%FFD^#;Mw@f0Nh3~hH4`g2z_t&Er`CSf%O8bQl~+zxRiFoY z7iJcc-=1msd?$L3$%^4Pq2*)X?6looA#I9O+;bUK;F5r$7%SMT1R9=Zn%yZ58a@SS z#1Og(&p6+dw#VNFAgAHh091O(vje7WdbI2+ zUXlpMzlfQ0pDpXKnx2-Je75a8a#IYqmB8p<`A?;&7)zC+T?8q`G^rh`rOrr(tUedZ ze}TC^-w$kgzy9ttq~sL`xb>C;E54jg*xT|LxmTur9yit@Fxj2k6;$4uJT<~tBk<)c zJ@s@a26KHz8H2NTiOT?g01` zv!JqU$DEAY_l_Va6mT1D1x3)UEk?5W$3IQi_T|XKWc6H5>KXZy1et;K+f{goL&6ed zhS5zSHWk$TCkZmcXv)o|{!aD1P0!4Q0}HwVyv@0nS-VnQD7gDRQOC?V3XEO(hN| zkRzN!ca#|0%Mf~y{EP%LOx2U-{?0Bl=eCyGW zuV=naOw!(hdf3vGO>kyf&eRv;c5ZF?M#L#FAIX6{MsNl+C@oDjGz6>nMmP7&q z?#rawEs%AFHB^T~FiHWUOPi9Ox(=KJshQou0SQyii7-ql(|GUaf7Xwmy%hfD2&oe3JO%oueprjy*Km{v{z*k zUQ4ki;SQ^341TUqd|e{E%7kFDKJJTntjsc(JfH^@*K*Ou?M^%Xs8!s5R{^0bEBdY< zj^ufAX#D{VZ;Hn+BQ93}-y?~b$h1O;b2s*0sG6qc-9qd#m|^7TKEq!P=1EH6N;c!w z5Rl9EBl6|tAs=}&B{Xn`ZjP;8wmdxRjltAGnR>wu`$)kAisvs2a3Z3)_kE?=d-D`T zqXNZ`8$kdqcFj5p!rgaYGg)h)ilUL%A>@?LTWHw^cOIoz8IEL8ddEdX> z%q;sV$rySguG5febbNs~_0zH|Kx^1XJ_VDi6wpV_b?@uY|Uze>h8q>K& zBP=>}xv3&@$&4s5V5Tt`*@9}ynZVqm8*NF%FoXgL4!DfJJh66eT8JYC_8`@ly6500 zop#tyjw4L!jkk2J{)Zw-@1*8qjl;w{H&^b!u_`Ad8&LDdK>@*GZyp;gKuJ(n00S_=Nto&l`m%!DNqt;EQcurg7kq<6AlYT}RDYil z2ql?LN_lG(Fr)CLQ<+EXrEw?*XLi$L13XKxH(YVgU;2EfG{oS{oZ)01ehkd>^^L7X zQbLF#MrCFoEflu;{HNOPt{{BiHGtJ8X2b`lqy%NHGDh(Z`-p0bEtDR*J~ywQv1^#( zDqr*k%j}~XfOohIS3qL%EN5}PSZbf0yQYG13pC*U01uWckZ46 zp;L7RfzS9^{{t6xS5tDP!y1}YcC!kWEiF$!W z=1vQmbsK@6k0BkWW`r)qaUM^C>XoiF^!gNcNlRPr68L;%U%kaJ_vHW$ZeKQ+h3KEh ziEnR|<(_#`%DDMsvGZ#xnRvQ?bJ`5Lv{+4XT%}1;0>+lc>9RMsc&OKAU3XIX2gN;n zFREx~5aQypp8q()-;0#AqtTFEd)^5t)m9*x48y;lswQ~Y>iuKU*=q03vsL|;d$?_xjm7G@p$FbwRHkb$}=3RpZLT(lVpty3QaNAuXTLZfi02QqH!&P{7J`zRT)&d+}LwHMC}43Sa@MY8IB| zoyO?QbY>1fCnI3D2V~!M*yZ!~Nj9^wBjSo%1rZ=#D4gW4{*0?`%x)f0{DPzKpcMQ- zO0OT__MYk#7HtgIO}JQ;_B!9?1uB!m&m@olnMDPxvayb}nf_<4m0;)ZE?Bbf>h4m>=O( z3O&}N3njBtUS;`(SwvW^Q&;aoGw`#(=n&8*y?snd6O1m`1!W6lpV8oA7Uph26=kC6 zkM@s+7_}H%nprPFB{5f%sisYU5GE~2Ctw8$>X}w?I&!Rcl6(AT(wLUc1tQ>xMRD?J&b_OWT z>SzONkFD8+5|OsqB1Q_2qi+H^Oobi5j9a{aU`*7Gt0etpMc$JHKu45-L_6Ic(K!rG z6iPeRF~%GBV@+V`5d;B>X2Z;a@?Ep=9Lx`TtTS~937kvD+Li3aU91Y!Q$Okd7K zMq*J}9sWkvfC8#uKk*HM1bB3q+b1US=W0ytsKoRv{H9qMflM}nEY+%_!8~Q|fxQZs zs1Cc*!iAVAq+^XOH_hC1KvekqhgfTZIac!2{r735^GzG9RlcPsOBb`AsM@Af+L0Hc zNWBPKTS53EiYq1S-5TILYhMLOjkY4kdz|BTGQg7V_R?tylRWZ_iwNhn0^xN)83^b|Dt(CsI$% z%;`sapfcqpz_Peb|7*{1y230W=E#czNm0-=s8Yn9s$wZ2;)V|w5`cq8+c5BRFK=Fm znfOdfdXIQqp#*(ZQDZTh2-eX-+-(D;hL zC~b-J%@jvk=@*ad)*^r4X+QVdSb{wTrNlm3PxVFjQ-_ZIP@;yf=`*DhcbDU%P+_D@$8hECfwnN!iiWn}#CsRQNixV3(4(rd7eFsu zwoVQ+iok_xI1NL#2L!1QSKy=yjvXv_#q5+LGsVV$%Jjyis4Nd#egqD!EI}Fwwo=vr zir`WhBSs4FY%PQpUi;mq25KCowMS+qJ?Mot=$=d`hxas&;;={{Ab?sB0G2whBtIUq zMG|K*L+Ji2su10yCk*pO;0R%60g;wYJoL)XaB*ljHEEUad7zM~*cf_N{cQb3v1u=*+{#@rYl_@HK#juOxJNx z)EM&%Ioq~QvMI(MG1V7GGOQ!aw#nbO`Ln%Rk`EzN;7c5`AEZ|fGwf^L9R9FU{V2gHcVJ$o&0M^sf23#aG6 zzC)xnrrTqxv7Cfr6nVsok{y=1Q6$J|vDD44y6$@mtJ)(HIo2Tu*l@DpP1%@b7sq!X zngpAhW}`Fg{7hJXAQ*T4`nxZrXQAAak-cj@6ql?}I=s8pBKg7DqO9wC(`Gfdt;64T zT~UyTLBmiD#(kf@r7YyY1QOver%7p@KN-idNkMUNtix^?_ScL%$>#ZDFl9|?3)D+V zy#O*KvfEm>nTFa=;P7L-0qsQ!qx>a+LU+chb=;d9LItHrV9yLxmNE=SO^pLQ~Ktv)n{D2+Qe)Iw57w7FZe$@>(un zG4dA^;V%JOTA6>l+UK}piNk4}YcjS333m5KTWxZea#BuB!euQCv%VacA?816s;juC$B_*@w4~5l#vYcMseYk2F z31OK!a`5TKnY^skrwkPk>q0(M*SC8}nl8Xv!)?CJVL&yK)i3!5t|ln}cd~%>oDDkO z-W?yDc=uc(*(-y?3ZEw9#CjYMA&^6L3$vgcSL-<>QxWiX z`4X;8aNcI@rN@UdchlMfBSs>fiYY?u*ov##3{kF%EGT$8?Yr);R@GZfVH!y#Ss($^ zZnb>!{zc(B;w+f<{Y0kaPzzU%adCS8dig8U2pn869*Q^|6{Nw|O)nTP!`R7`ld})% zcRO6O1`Pz!0=Ra4{k-G;#YdR+j2E#3+l5~HPzh&~RB4Bk^(0!NW#~>Et_tlRj~w#0 zh*?{($F2y%Q7wBAiAJhrliNP~x$E-IT-%IZWn(Hzux7Idfu`s;8?%p|%ut*(e4A5B^H63WIWgKA0Fdu! zVPBj7dG*=rOYo~kg1?(bqhD!2!Bfqv>Z{4xaPEr6G|VtkC%zF+w<-$(zh@uyKe7t3 zf!2(`Hp=4vP5@na>X=vaEmzT>Dk8f8Z(p%8Bdk2ZUn<8qynhoPe*3-w`8Lcssu<$= zMnEq6<4N_V!ZH<5!)w8p8^AR=7uvJxA zUI!JC@5BeBG{5LRNovVDdy0TVoz~M)R>+?s0T(+`Lw(W?Z=vFyB&g~tOQ8^*zgT0x zjAxIvGN`HMj%J&EbBWC{x&$a%|015jIL#z?@uz#(Zdg;|9hac$4mzxQV3JJ;IyL}R zGqU4ckqtgI=KA@TSBFA$Y$#1P$d3ZN%&vO7!?H_@0#H%r^%nlDv$IVt%+LV*^Vm8- zVLThH!FFho7atFS;rC)LRv03SgM9|IK*_k5q&~v%Rfra&UAUiJ9W7wL%=Vvc+twmU z_Wr-v?}*c?9)Nrn^PL>Ux7`-8Sj1>n>cxc2h~^roKI**Bz>>Qgy)Pq1;a2=C97Z zkpkmMQr!BJRB6aj#G54%f1vo6ajtO_#pMV1BzRZ6EKboo8l4^q&dZtxo8+-lC`pGs zI$Sv9`RLfRzS;-}&F&_@eq3TV?WSo&G7mN2;w^K?U*#+=<^)9bx)<&^B#O6)k7%&( ze1HXICBUZ3U+l8iS#I7OuM{kG9&hQUH3^7j985!|3j}Smf;gn#xHW+4mc^6 zu9DNlYSOrXN_n+RlxXK`P*0CpX#vJ6`P**I3WjZa{2FsRIXi<6cr-U8dfanXwITHv zhQ?qs1u$u|tzvETbnAGsX%AazH%M|9`0RLost3xezPoAnJeAsn_srn^#fGc_NFeKA z^*mirQr%m1HwSS=-hnLx+J&fjilPo3?!SKo-`?O+vlO72Jr>KXIfWc^&pSUOuLY$hz?(n ze3}_X>ixD*Pf=Ems*P89b+?Sv3>kI-&I#CyXb%t{^H@p_(!tUdXc+K4%_1pmf5g}l zFkx&6Rz2#WU!_uGeam+jeFmy`H+r?itb=Gv|52$oS_U(0`3q!HhAL1MRKRyyxt9ju zQ|}%qdSXtC#OQBlx}A&u?meFdfB!#p_^Kwby?{Lz8ccc;Ey<$~znVpmW}}m#vZ4?Fe_M28d}iDaF;cxk1OH6g++$4SW+QYTT~@dmfLLa!| zF8ce|9i06{j2X|DBkBJ@_~)^_3`(#8<0$-xAn)agImfr(d~e{}0AC_XOCD3pW5? z?iY45bLrQ%QCp1z%IwUm?)y*G^kX##s`yfL;<1Ab}Pb;dr2esFL60U7L! zwzX_wqZ1(*{=cu1lzh<*;pu;=L#}so8_ouLFB(trq41<+I%i$_OhVv^uJ*0%Z*h^X^FxM z*f$vTcq*C}UHqb77_y=9k{^$`(~r=Qol!VNVC$NQd!f%3|3)wNy{tPdcg1FQZ?Dh4 zm`+%;cyDLB*9;_ORNT?|=4r6zTyh`;?zHynQ*3tbhnu1Xzj7Jti$H6_du{CW#Xgqt z%v;~QoBXRV;nrR-g63XcFL~UjEc{{a%~ss|06@*pVlUJMF7^H@C;v;Ey<+vhF`%1r z$aZ^G;=9r6Z}WZy2;p9XV?BmUK)mVdsefB~rgZ5L-Wr>nHFE@c`xWl$XMWZB?FXPbuMF8>2^Z(J1oV(R%oMo3r^lxsFQFUSvKd{KFVR4TlRk zqPvOF%@UqX+)q8jk46`>y$3OcFo+p``(oGU=+-~@K zb-}~$wwyrN5}}_9Q8jV;z`Ys+jpc7Ru{G*{k@4X~=#lN4EE5FZ$)Ww1n4nA;vl47wftZ-nHqYg~#7$Kzz*-`Z1aQ zeWbBtOp`+lgk{AO;*NAw-dgU3HW&pSeF`WV&N(-Mbd;&AknzG(pXt5vO=GajQ2HeT zhyY>r24(!7Z8wNhB>C&x56~k|s3nNpbGx`C`V(FCFYYG_;gB%x*7v-sf|=Y!+(3-* z!y8{u!n+_ddSfwR0v4L=b`~1*ufegO*L~nd5g0$bsNZJ=%)EZLvtGIrVqnW=@#>ae zEX#g-XCaiJrFy3nHoE(<>2*+klIm4o;fLezB-e?FoEPu?r8Hr+|7K$9e3Z!@&}+-W ztK6hK&7B@eL=?iHC)RxBR$>^kcnAGXLi7;CH_hN3hbg=buP+FBcfo=Gud1sLimHm@ zZ{$XDbeO8RKX9=&EkD=Ro6VwupVa8+UPilooDq ztqt@PxrM?%tx36u;^)%cR_qL0PJ1~XECUcbmMXvHUDWmw+x$#2A;jLHyd*X>PUr$; zbhneQO5a@0eRF2nu~ii|=#lg4p|hQHJPiYjzyxwxF^Epp2787vT$&WNG7bMD|f7*7fnKjmNnNPl$f& zIUYTzje{7lqbWQ(J@>em?_ujcQ;ukAP7a(9S&G&`b(dzzJnQdnaD z4F-Jxl4a%&Iav6(H##Dd=d@$wpk@u3h)2=J3VV*${$ITpZ81oE_3V8k2%d+2fLT@% z#R}evvVq|3NxZG+G=Hp%ftIlQx!ezo(vO~Thc;%Dmijl_!US(dDm;Z0 z*;`w7Mr|D9Fd!aUBEN6uyDVzNsEubF1dGih1=rF{Vaz;!+H^0e&r7?20s%gQs@v$+P%LRa;V zs22pbAfW3RMPDA&fOjX88`B}90B|DU@6$o1dT285WVhUozLrU9`H2(jS+{$!VD?Q1 z?Sw`f(Dhcf?V%L{c^wMLsH=QF7u!?LVY4gdKp&-UHHPz2#&7U<0M9}Wp?fjH|8y+l zpl~kVGn_P~70AQ=PZeQT%+Kp!U_W%=5{||i`Hrw(WKS7I-Q`b<1LK(3am!*CW`%3T zfzTZcR{PO~kh)UfdjRvwF}%@B z`e&x3_9@HEL>re${UqJIJ2ldt;@%4fnM6|I+nPaE)!r;6oSt;Mt_$*^bCvk+C@9|j z$Om)jA{#1FJuI>~f};hs0h7tmh&rMAA~rPAZc8hhvx(zo(+B9D1XXO|(#qwz#_aFZ zqT58T%+zicyX`voXWPsWD zi4mr!v^(k|=isg*&=3pqNUBdX1Fs8?R1w-ys57 z`uA!TN?{e=3>{T6rtu)5{xEqx-t+Plj<$oGccfAIlMH26kJ-$RM@BCGnTh>An4VO4id#ZLg>i#kf0nV>+Duu^r6jv#!K-nqXKt?}GXzQ1vh{+sVmleo zA4Uxg=nDN>;ip_vrCExww!`fNY=f*1PCLsrYHYbbw)2a<_^Mgx=@C}TOiyMO#3hEQ5&hJo4|J+UEHF$z3IDM(Aks8bR2JPA0psK6Z=s}>J!oK0 rJ6N-&kq)AmJ;{(~7}y2>K78mybsF1if}UP?3zAgMs{G9J8MNs?67?t+ diff --git a/PYTHON APPS/DownTube/README.md b/PYTHON APPS/DownTube/README.md deleted file mode 100644 index 0902daf4..00000000 --- a/PYTHON APPS/DownTube/README.md +++ /dev/null @@ -1,35 +0,0 @@ -# DownTube - -## Introduction - -**DownTube** is a piece of GUI based program which downloads Youtube videos either in 360p or 720p resolution. Very simple User Interface. - ---- - -## Packages - -Refer [requirements.txt](requirements.txt) for python packages used to develop this program. - ---- - -## Features - -**Features of this application:** - -* Just paste the complete URL(***from search bar of the browser***) of the video you want to download. - -* You can store the downloaded video whereever you want(***just click browse button***) - -* Choose the available resolution(either 360p or 720p). - ---- - -## Information to users - -* If videos aren't downloaded, Try to update or reinstall `pytube` package. Since pytube had issues in downloading the videos from YouTube in the past. - -* If this program shows not responding in windows, kindly don't close the application. Since this the sign of downloading the videos. - -* This program was tested on Windows machines and works completely fine. But it may or may not work on other operating system platforms like(Linux or MacOS, etc.,). - ---- diff --git a/PYTHON APPS/DownTube/downtube.py b/PYTHON APPS/DownTube/downtube.py deleted file mode 100644 index 51cb9c95..00000000 --- a/PYTHON APPS/DownTube/downtube.py +++ /dev/null @@ -1,101 +0,0 @@ -import tkinter as tk -from tkinter import ttk -from tkinter.constants import RAISED - -# Importing the user defined module. -import Functionality as fn - -def main(): - - # Root window - main_window = tk.Tk() - main_window.title("Downtube") # Title of the window - main_window.minsize(600, 400) # Default Window size - main_window.rowconfigure(0,weight=1) # Rowconfiguration of root window in order to expand widgets, when window is is resized. - main_window.columnconfigure(0, weight= 1) # Rowconfiguration of root window in order to expand widgets, when window is is resized. - main_window.rowconfigure(1,weight=1) # Rowconfiguration of root window in order to expand widgets, when window is is resized. ### - main_window.columnconfigure(1, weight= 1) # Rowconfiguration of root window in order to expand widgets, when window is is resized. ### - main_window.configure(bg= "white") # Background color for root window - -# Parent Frame widgets: - - # Input frame widget. - main_frame = tk.Frame(main_window, borderwidth= 2,bg= "white") - main_frame.grid(row= 0, column= 0, columnspan= 3, rowspan= 4) - - # Configuration of row and column of "main_frame" in order to expand widgets, when window is is resized. - main_frame.columnconfigure(0, weight= 1) - main_frame.columnconfigure(1, weight= 1) - main_frame.columnconfigure(2, weight= 1) - main_frame.rowconfigure(0, weight= 1) - main_frame.rowconfigure(1, weight= 1) - main_frame.rowconfigure(2, weight= 1) - main_frame.rowconfigure(3, weight= 1) - -# main_frame widgets - - # Display label indicating --> 'paste the youtube link'. - linke_label = tk.Label(main_frame, text= "Paste the YouTube link", width= 40, borderwidth= 1, anchor= "w", bg= "white") - linke_label.grid(row= 0, column= 1, sticky="WE", pady= 2) - - # Display label indicating --> 'Browse to save the file'. - linke_label = tk.Label(main_frame, text= "Browse to save the file", bg= "white", width= 40, anchor= "w") - linke_label.grid(row= 2, column= 1) - - # Display label indicating --> 'Choose the resolution'. - resolution_lb = tk.Label( - main_frame, text= "Choose the resolution", width= 15, - height= 1, anchor= "w", bg= "white" - ) - resolution_lb.grid(row=4, column= 1, pady=2, sticky= "we") - - - # Entry Widget --> Getting youtube link. - link = tk.StringVar() - get_link = tk.Entry(main_frame, textvariable= link, bg= "white") - get_link.grid(row= 1, column= 1, sticky= "wE", pady= 2) - - # Entry Widget --> Getting Directory to save file. - directory = tk.StringVar() - get_dir = tk.Entry(main_frame,textvariable= directory, bg= "white", fg= "grey") - get_dir.grid(row= 3, column= 1, sticky= "wE") - get_dir.insert(0, "Choose a folder") - - # Combo box Widget --> Shows the options(Resolution) available. - my_string_var = tk.StringVar() - resolution_box = ttk.Combobox( - main_frame, textvariable=my_string_var, - values=["360p", "720p"]) - resolution_box.grid(row=5, column= 1, pady=2, sticky= "we") - - # Button widget --> Clear the Input field of youtube entry widget - clear_bt = tk.Button( - main_frame, text= "Clear", - width= 10, height= 1, - bg= "grey", - command= lambda: fn.clear(link, get_link) - ) - clear_bt.grid(row= 1, column= 2, pady= 2) - - # Button widget --> Opens file explorer to save the file - temp_bt = tk.Button( - main_frame, text= "Browse", - relief= RAISED, width= 10, - height= 1, bg= "grey", - command= lambda :fn.browse_folder(get_dir) - ) - temp_bt.grid(row=3, column= 2, pady=2) - - # Button widget --> Downloads the video - download_file = tk.Button( - main_frame, text= "Download", - relief= RAISED, width= 10, - height= 1, bg= "grey", anchor= "center", - command= lambda :fn.download_bt(link.get(), get_link, resolution_box.get(), directory.get(), get_dir, resolution_box) - ) - download_file.grid(row=5, column= 2, pady=2) - - main_window.mainloop() - -if __name__ == '__main__': - main() diff --git a/PYTHON APPS/DownTube/requirements.txt b/PYTHON APPS/DownTube/requirements.txt deleted file mode 100644 index 0ff08030d864dc8c097638cae7a5ac3f610fde01..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 56 tcmezWuYjSFp@gB7A&DWC!4?P&8H^b8fTSLS0Rt}s7f>!6EN*}z3II)42&Di3 From 0da58fcdcd8cdadd5373ad5788ca9ef052c200de Mon Sep 17 00:00:00 2001 From: senthilnathan Date: Thu, 20 Oct 2022 20:54:32 +0530 Subject: [PATCH 5/6] added downtube --- PYTHON APPS/DownTube/Exceptions.py | 18 +++ PYTHON APPS/DownTube/Functionality.py | 138 ++++++++++++++++++ .../DownTube/Props/YT_icon_ico_64x64.ico | Bin 0 -> 11942 bytes .../DownTube/Props/YT_icon_png_64x64.png | Bin 0 -> 36544 bytes PYTHON APPS/DownTube/README.md | 35 +++++ PYTHON APPS/DownTube/downtube.py | 101 +++++++++++++ PYTHON APPS/DownTube/requirements.txt | Bin 0 -> 56 bytes 7 files changed, 292 insertions(+) create mode 100644 PYTHON APPS/DownTube/Exceptions.py create mode 100644 PYTHON APPS/DownTube/Functionality.py create mode 100644 PYTHON APPS/DownTube/Props/YT_icon_ico_64x64.ico create mode 100644 PYTHON APPS/DownTube/Props/YT_icon_png_64x64.png create mode 100644 PYTHON APPS/DownTube/README.md create mode 100644 PYTHON APPS/DownTube/downtube.py create mode 100644 PYTHON APPS/DownTube/requirements.txt diff --git a/PYTHON APPS/DownTube/Exceptions.py b/PYTHON APPS/DownTube/Exceptions.py new file mode 100644 index 00000000..aae18fbc --- /dev/null +++ b/PYTHON APPS/DownTube/Exceptions.py @@ -0,0 +1,18 @@ +class Downtube_Error(Exception): + """ This is the base exception class for all other user defined exception(classes) """ + pass + +class Link_Error(Downtube_Error): + """ When there is no Link is given as input """ + pass + +class InvalidLink(Downtube_Error): + """ When the ink URL isn't a youtube link """ + pass + +class DirectoryError(Downtube_Error): + """ when No directory is given """ + pass +class ResolutionError(Downtube_Error): + """ when no resolution is givenn """ + pass \ No newline at end of file diff --git a/PYTHON APPS/DownTube/Functionality.py b/PYTHON APPS/DownTube/Functionality.py new file mode 100644 index 00000000..868e27a6 --- /dev/null +++ b/PYTHON APPS/DownTube/Functionality.py @@ -0,0 +1,138 @@ +import re +from tkinter.constants import END +import downtube as dt +import Exceptions as ex +from tkinter import messagebox as msg +from tkinter import filedialog as fd +import urllib.request +import pytube as pt +from pytube import YouTube + + +def clear(link, get_link): + """ Clears the Entry box widget """ + download_link = link.get() + if download_link == "": + msg.showwarning( + title= "Warning", + message= "Warning: The input field is already empty", + ) + else : + get_link.delete(0,last= len(download_link)) + +def browse_folder(get_dir): + global dir_path + dir_path = fd.askdirectory() + get_dir.delete(0, END) + get_dir.configure(fg= "black") + get_dir.insert(0, dir_path) + if dir_path == "": + get_dir.configure(fg= "grey") + get_dir.insert(0, "Choose a folder") + +def check_connectivity(link): # Referred website: codespeedy (https://www.codespeedy.com/how-to-check-the-internet-connection-in-python/) + """ Checks for internet connectivity or valid youtube link """ + try: + urllib.request.urlopen(link) + except: + msg.showerror( + title= "Network Error", + message= "Connectivity issue found", + detail= "Check you internet connectivity \nor\n the link might not be correct" + ) + +def dwn(download_link, video_resolution, directory): + """ Downloads the video """ + yt = pt.YouTube(download_link) + try: + if video_resolution == "": + raise ex.ResolutionError + except ex.ResolutionError: + msg.showerror(title= "Downtube", + message= "No video resolution found" + ) + if video_resolution == "360p": + try: + video = yt.streams.filter(progressive= True, file_extension= "mp4", res= "360p", type= "video").first() + if video == None: + raise AttributeError + else: + return video + #video.download(output_path= directory) + except AttributeError: + msg.showinfo(title= "Downtube", + message= "The video resolution is not available to be downloaded ", + detail= "Try downloading with other resolution" + ) + elif video_resolution == "720p": + try: + video = yt.streams.filter(progressive= True, file_extension= "mp4", res= "720p", type= "video").first() + if video == None: + raise AttributeError + else: + return video + #video.download(output_path= directory) + except AttributeError: + msg.showinfo(title= "Downtube", + message= "The video resolution is not available to be downloaded ", + detail= "Try downloading with other resolution" + ) + +def clear_inputs(get_link, get_resolution): + input_of_get_link = get_link.get() + input_of_get_resolution = get_resolution.get() + get_link.delete(0,last= len(input_of_get_link)) + get_resolution.delete(0,last= len(input_of_get_resolution)) + + + +def download_bt(download_link, get_link, video_resolution, directory, get_dir, resolution_box ): + """ All the process for downloading will be done here """ + verify_YT = re.search(r"youtube.com", download_link) # Verifying youtube link or not + + + try: + """ Checks for youtube link and rasie error if link is not valid or not found """ + if download_link == "": + raise ex.Link_Error + elif verify_YT == None: + raise ex.InvalidLink + else: + check_connectivity(download_link) + get_link.delete(0,last= len(download_link)) + try: + if directory == "": + raise ex.DirectoryError + else: + vid = dwn(download_link, video_resolution, directory) + vid.download(directory) #type: ignore + clear_inputs(get_dir, resolution_box) + except ex.DirectoryError: + msg.showerror(title= "Directory error", + message= "Directory Error: No Directory is found", + detail= "Browse or enter the directory to save the file" + ) + except ex.Link_Error: + msg.showerror( + title= "Link Error", + message= "Youtube link Not Found", + detail= "You've not given any link in the \"Link section\"" + ) + except ex.InvalidLink: + msg.showerror( + title= "Invalid link", + message= "Error: Invalid link", + detail= "The given link is invalid or doesn't belongs to youtube" + ) + + try: + if directory == "Choose a folder" : + raise ex.DirectoryError + except ex.DirectoryError: + msg.showerror( + title = "Directory Error", + message="Error: No directory is given to save the file" + ) + +if __name__=='__main__': + dt.main() diff --git a/PYTHON APPS/DownTube/Props/YT_icon_ico_64x64.ico b/PYTHON APPS/DownTube/Props/YT_icon_ico_64x64.ico new file mode 100644 index 0000000000000000000000000000000000000000..7b5b87ab79acb5c92452645d980559151dba1eb7 GIT binary patch literal 11942 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 literal 0 HcmV?d00001 diff --git a/PYTHON APPS/DownTube/README.md b/PYTHON APPS/DownTube/README.md new file mode 100644 index 00000000..0902daf4 --- /dev/null +++ b/PYTHON APPS/DownTube/README.md @@ -0,0 +1,35 @@ +# DownTube + +## Introduction + +**DownTube** is a piece of GUI based program which downloads Youtube videos either in 360p or 720p resolution. Very simple User Interface. + +--- + +## Packages + +Refer [requirements.txt](requirements.txt) for python packages used to develop this program. + +--- + +## Features + +**Features of this application:** + +* Just paste the complete URL(***from search bar of the browser***) of the video you want to download. + +* You can store the downloaded video whereever you want(***just click browse button***) + +* Choose the available resolution(either 360p or 720p). + +--- + +## Information to users + +* If videos aren't downloaded, Try to update or reinstall `pytube` package. Since pytube had issues in downloading the videos from YouTube in the past. + +* If this program shows not responding in windows, kindly don't close the application. Since this the sign of downloading the videos. + +* This program was tested on Windows machines and works completely fine. But it may or may not work on other operating system platforms like(Linux or MacOS, etc.,). + +--- diff --git a/PYTHON APPS/DownTube/downtube.py b/PYTHON APPS/DownTube/downtube.py new file mode 100644 index 00000000..51cb9c95 --- /dev/null +++ b/PYTHON APPS/DownTube/downtube.py @@ -0,0 +1,101 @@ +import tkinter as tk +from tkinter import ttk +from tkinter.constants import RAISED + +# Importing the user defined module. +import Functionality as fn + +def main(): + + # Root window + main_window = tk.Tk() + main_window.title("Downtube") # Title of the window + main_window.minsize(600, 400) # Default Window size + main_window.rowconfigure(0,weight=1) # Rowconfiguration of root window in order to expand widgets, when window is is resized. + main_window.columnconfigure(0, weight= 1) # Rowconfiguration of root window in order to expand widgets, when window is is resized. + main_window.rowconfigure(1,weight=1) # Rowconfiguration of root window in order to expand widgets, when window is is resized. ### + main_window.columnconfigure(1, weight= 1) # Rowconfiguration of root window in order to expand widgets, when window is is resized. ### + main_window.configure(bg= "white") # Background color for root window + +# Parent Frame widgets: + + # Input frame widget. + main_frame = tk.Frame(main_window, borderwidth= 2,bg= "white") + main_frame.grid(row= 0, column= 0, columnspan= 3, rowspan= 4) + + # Configuration of row and column of "main_frame" in order to expand widgets, when window is is resized. + main_frame.columnconfigure(0, weight= 1) + main_frame.columnconfigure(1, weight= 1) + main_frame.columnconfigure(2, weight= 1) + main_frame.rowconfigure(0, weight= 1) + main_frame.rowconfigure(1, weight= 1) + main_frame.rowconfigure(2, weight= 1) + main_frame.rowconfigure(3, weight= 1) + +# main_frame widgets + + # Display label indicating --> 'paste the youtube link'. + linke_label = tk.Label(main_frame, text= "Paste the YouTube link", width= 40, borderwidth= 1, anchor= "w", bg= "white") + linke_label.grid(row= 0, column= 1, sticky="WE", pady= 2) + + # Display label indicating --> 'Browse to save the file'. + linke_label = tk.Label(main_frame, text= "Browse to save the file", bg= "white", width= 40, anchor= "w") + linke_label.grid(row= 2, column= 1) + + # Display label indicating --> 'Choose the resolution'. + resolution_lb = tk.Label( + main_frame, text= "Choose the resolution", width= 15, + height= 1, anchor= "w", bg= "white" + ) + resolution_lb.grid(row=4, column= 1, pady=2, sticky= "we") + + + # Entry Widget --> Getting youtube link. + link = tk.StringVar() + get_link = tk.Entry(main_frame, textvariable= link, bg= "white") + get_link.grid(row= 1, column= 1, sticky= "wE", pady= 2) + + # Entry Widget --> Getting Directory to save file. + directory = tk.StringVar() + get_dir = tk.Entry(main_frame,textvariable= directory, bg= "white", fg= "grey") + get_dir.grid(row= 3, column= 1, sticky= "wE") + get_dir.insert(0, "Choose a folder") + + # Combo box Widget --> Shows the options(Resolution) available. + my_string_var = tk.StringVar() + resolution_box = ttk.Combobox( + main_frame, textvariable=my_string_var, + values=["360p", "720p"]) + resolution_box.grid(row=5, column= 1, pady=2, sticky= "we") + + # Button widget --> Clear the Input field of youtube entry widget + clear_bt = tk.Button( + main_frame, text= "Clear", + width= 10, height= 1, + bg= "grey", + command= lambda: fn.clear(link, get_link) + ) + clear_bt.grid(row= 1, column= 2, pady= 2) + + # Button widget --> Opens file explorer to save the file + temp_bt = tk.Button( + main_frame, text= "Browse", + relief= RAISED, width= 10, + height= 1, bg= "grey", + command= lambda :fn.browse_folder(get_dir) + ) + temp_bt.grid(row=3, column= 2, pady=2) + + # Button widget --> Downloads the video + download_file = tk.Button( + main_frame, text= "Download", + relief= RAISED, width= 10, + height= 1, bg= "grey", anchor= "center", + command= lambda :fn.download_bt(link.get(), get_link, resolution_box.get(), directory.get(), get_dir, resolution_box) + ) + download_file.grid(row=5, column= 2, pady=2) + + main_window.mainloop() + +if __name__ == '__main__': + main() diff --git a/PYTHON APPS/DownTube/requirements.txt b/PYTHON APPS/DownTube/requirements.txt new file mode 100644 index 0000000000000000000000000000000000000000..0ff08030d864dc8c097638cae7a5ac3f610fde01 GIT binary patch literal 56 tcmezWuYjSFp@gB7A&DWC!4?P&8H^b8fTSLS0Rt}s7f>!6EN*}z3II)42&Di3 literal 0 HcmV?d00001 From 778135c4f25302ee751054aa0ff723233844f5ce Mon Sep 17 00:00:00 2001 From: senthilnathan Date: Thu, 27 Oct 2022 18:43:22 +0530 Subject: [PATCH 6/6] added sample GUI --- PYTHON APPS/DownTube/Props/sample_GUI.jpg | Bin 0 -> 22023 bytes PYTHON APPS/DownTube/README.md | 4 ++++ 2 files changed, 4 insertions(+) create mode 100644 PYTHON APPS/DownTube/Props/sample_GUI.jpg diff --git a/PYTHON APPS/DownTube/Props/sample_GUI.jpg b/PYTHON APPS/DownTube/Props/sample_GUI.jpg new file mode 100644 index 0000000000000000000000000000000000000000..868c3c0de5bd5a465e588287b425ae101c159552 GIT binary patch literal 22023 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 F{|napU={!X literal 0 HcmV?d00001 diff --git a/PYTHON APPS/DownTube/README.md b/PYTHON APPS/DownTube/README.md index 0902daf4..8de17885 100644 --- a/PYTHON APPS/DownTube/README.md +++ b/PYTHON APPS/DownTube/README.md @@ -33,3 +33,7 @@ Refer [requirements.txt](requirements.txt) for python packages used to develop t * This program was tested on Windows machines and works completely fine. But it may or may not work on other operating system platforms like(Linux or MacOS, etc.,). --- + +## Sample GUI + +![sample UI](/PYTHON%20APPS/DownTube/Props/sample_GUI.jpg)