Skip to content

Added DownTube (A GUI based YouTube downloader) #223

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 8 commits into from
Nov 3, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 18 additions & 0 deletions PYTHON APPS/DownTube/Exceptions.py
Original file line number Diff line number Diff line change
@@ -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
138 changes: 138 additions & 0 deletions PYTHON APPS/DownTube/Functionality.py
Original file line number Diff line number Diff line change
@@ -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()
Binary file added PYTHON APPS/DownTube/Props/YT_icon_ico_64x64.ico
Binary file not shown.
Binary file added PYTHON APPS/DownTube/Props/YT_icon_png_64x64.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added PYTHON APPS/DownTube/Props/sample_GUI.jpg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
39 changes: 39 additions & 0 deletions PYTHON APPS/DownTube/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
# 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.,).

---

## Sample GUI

![sample UI](/PYTHON%20APPS/DownTube/Props/sample_GUI.jpg)
101 changes: 101 additions & 0 deletions PYTHON APPS/DownTube/downtube.py
Original file line number Diff line number Diff line change
@@ -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()
Binary file added PYTHON APPS/DownTube/requirements.txt
Binary file not shown.