Skip to content

Commit

Permalink
v0.0.1
Browse files Browse the repository at this point in the history
v0.0.1
  • Loading branch information
Thisal-D authored May 8, 2024
1 parent bc80417 commit 77f887a
Show file tree
Hide file tree
Showing 37 changed files with 2,621 additions and 0 deletions.
21 changes: 21 additions & 0 deletions LICENSE
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
MIT License

Copyright (c) 2024 Thisal Dilmith

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
84 changes: 84 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
🌟**If you find this project helpful, your support by starring it would mean a lot! Your feedback motivates me to keep refining and enhancing it further.** 🚀

Your support means a lot and inspires me to do better with each update. Thank you for taking the time to check out this project!🥰

<hr>

# PyTube Downloader

<a href="https://drive.usercontent.google.com/download?id=1MBn13dgxY0qTajxJVrjcM_vxTQKAORQW&export=download&authuser=0&confirm=t&uuid=7f7f3a7e-cc5f-4b44-8d21-75863cc10a30&at=APZUnTXf30S6xNS7lOvS1c8dvXL4:1711541083027">**Download .exe for Windows**</a>


PyTube Downloader is a user-friendly application that allows users to download YouTube videos with ease. It features a simple and intuitive user interface, making the downloading process straightforward for all users.

<hr>

## Features

- **Easy Downloading :** Download YouTube videos effortlessly by pasting the video URL into the application.
- **Playlist Downloading :** Download entire playlists using just the playlist URL
- **Format Selection :** Choose from various video and audio formats for downloading.
- **Progress Tracking :** Track the download progress within the application.
- **Customization Options :** Customize download locations and theme and colors


<hr>

### Dark theme view

<img src="https://github.com/Thisal-D/PyTube-Downloader/assets/93121062/6041696e-bdfd-43ca-b69b-3e2241b0a9bd" style="width: 100%">
<img src="https://github.com/Thisal-D/PyTube-Downloader/assets/93121062/6041696e-bdfd-43ca-b69b-3e2241b0a9bd" style="width: 100%">
<img src="https://drive.google.com/thumbnail?id=1pH5kYvvAsnqcFJlB9kxo1cNjvfQq0oGi&sz=w970" style="width: 100%">

<hr>

## Technologies Used

- **Programming Language:** [Python]
- **Frameworks/Libraries:** [tkinter, customtkinter, pytube, pillow ]

<hr>

## How to Use

1. Clone the repository to your local machine.
2. Install the necessary dependencies (if any).
3. Run the application.
4. Paste the YouTube video URL into the designated field.
5. Choose the desired format
6. Click the download button to initiate the download process.
7. Monitor the download progress within the application.
8. Enjoy your downloaded YouTube video!


<hr>

## Contribution

Contributions to this project are welcome! Feel free to fork the repository, make improvements, and submit pull requests.

<hr>

## License

This project is licensed under the MIT License - see the [LICENSE](LICENSE) file for details.

<hr>

## Disclaimer

This application is intended for personal use only. Please respect YouTube's terms of service and the rights of content creators when downloading videos.


## Contributors

- [<img src="https://github.com/childeyouyu.png?size=25" width="25">](https://github.com/childeyouyu)
[youyu](https://github.com/childeyouyu)


- [<img src="https://github.com/Navindu21.png?size=25" width="25">](https://github.com/Navindu21)
[Navindu Pahasara](https://github.com/Navindu21)

- [<img src="https://github.com/sooryasuraweera.png?size=25" width="25">](https://github.com/sooryasuraweera)
[Soorya Suraweera](https://github.com/sooryasuraweera)

1 change: 1 addition & 0 deletions VERSION
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
VERSION = '0.0.1'
415 changes: 415 additions & 0 deletions app.py

Large diffs are not rendered by default.

3 changes: 3 additions & 0 deletions dependencies_installer.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
import os

os.system("pip install -r requirements.txt")
9 changes: 9 additions & 0 deletions functions/convertTime.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
def convertTime(s: int) -> str:
h = int(s/3600)
m = int((s - (h*3600)) / 60)
s = s - (h*3600) - (m*60)
if h>0:
converted_time = f"{h}:{m:0>2}:{s:0>2}"
else:
converted_time = f"{m}:{s:0>2}"
return converted_time
12 changes: 12 additions & 0 deletions functions/createDownloadDirectory.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
import os

def createDownloadDirectory(path):
try:
if not os.path.exists(path):
sub_paths = path.split("\\")
for i in range(0, len(sub_paths)):
sub_path = "\\".join(sub_paths[0:i+1])
if not os.path.exists(sub_path):
os.mkdir(sub_path)
except Exception:
raise BufferError("download path errror :/")
8 changes: 8 additions & 0 deletions functions/formatToComboBoxValues.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
from .getConvertedSize import getConvertedSize

def formatToComboBoxValues(supported_download_types):
reso_box_values = []
for data_dict in supported_download_types:
for data_key in data_dict:
reso_box_values.append(data_key + " | " + getConvertedSize(data_dict[data_key] ,1))
return (reso_box_values)
4 changes: 4 additions & 0 deletions functions/getColor.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
def getColor(color, theme):
if theme == "Dark":
return color[1]
return color[0]
2 changes: 2 additions & 0 deletions functions/getConvertedPath.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
def getConvertedPath(path):
return path.replace("/","\\")
13 changes: 13 additions & 0 deletions functions/getConvertedSize.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
def getConvertedSize(s,with_decimal):
datas = ["B","KB","MB","GB","TB","PB","EB"]
index= 0

while len(str(int(s))) > 3 and (index+1) < len(datas) :
s = s /1024
index += 1
if with_decimal > 0:
val = str(round(s,with_decimal)) + " " + datas[index]
else:
val = str(int(s)) + " " + datas[index]

return val
6 changes: 6 additions & 0 deletions functions/getGeneralSettings.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
import json

def getGeneralSettings():
file_name = ".\\settings\\general.json"
settings = json.load(open(file_name, "r"))
return settings
31 changes: 31 additions & 0 deletions functions/getSupportedDownloadTypes.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
import pytube

def toDict(data):
data_list = []
for d in data:
data_list.append({value.split("=")[0]:value.split("=")[1] for value in str(d)[9:-1].replace('"',"").split(" ")})
return data_list


def getSupportedDownloadTypes(video_streams: pytube.StreamQuery):
supportedDownloadTypes = []
data = toDict(video_streams.all())
for type_info in data:
if type_info["type"] == "video":
try:
file_size = video_streams.get_by_resolution(type_info["res"]).filesize
download_info = {type_info["res"] : file_size}
if download_info not in supportedDownloadTypes:
supportedDownloadTypes.append(download_info)
except:
#print("Error :",type_info["res"])
pass
try:
audio_only = video_streams.get_audio_only()
file_size = audio_only.filesize
bitrate = str(int((audio_only.bitrate)/1024)) + "kbps"
supportedDownloadTypes.append({bitrate: file_size})
except:
pass
#print("Error :","Audio")
return supportedDownloadTypes
9 changes: 9 additions & 0 deletions functions/getThemeSettings.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
import json

def getThemeSettings():
file_name = ".\\settings\\theme.json"
settings = json.load(open(file_name, "r"))
for key in settings.keys():
if type(settings[key]) == list:
settings[key] = tuple(settings[key])
return settings
65 changes: 65 additions & 0 deletions functions/getThumbnails.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
from .getValidFileName import getValidFileName
from .removeInvalidCharts import removeInvalidChars
from urllib import request
import tkinter
from PIL import Image, ImageDraw

def add_corners(im, rad):
circle = Image.new('L', (rad * 2, rad * 2), 0)
draw = ImageDraw.Draw(circle)
draw.ellipse((0, 0, rad * 2 - 1, rad * 2 - 1), fill=255)
alpha = Image.new('L', im.size, 255)
w, h = im.size
alpha.paste(circle.crop((0, 0, rad, rad)), (0, 0))
alpha.paste(circle.crop((0, rad, rad, rad * 2)), (0, h - rad))
alpha.paste(circle.crop((rad, 0, rad * 2, rad)), (w - rad, 0))
alpha.paste(circle.crop((rad, rad, rad * 2, rad * 2)), (w - rad, h - rad))
im.putalpha(alpha)
return im


def getHoverThumbnail(thumbnail_download_path):
thumbnail_hover_temp = Image.open(thumbnail_download_path)
thumbnail_hover_path = ".".join(thumbnail_download_path.split(".")[0:-1]) + "-hover." + thumbnail_download_path.split(".")[-1]
thumbnail_hover_temp = thumbnail_hover_temp.convert("RGB")
thumbnail_hover_data = thumbnail_hover_temp.getdata()
thumbnail_hover_data_list = []
for item in thumbnail_hover_data:
item = list(item)
for index ,i in enumerate(item):
if i+30 < 256 :
item[index] = i+30
else:
item[index] = 255
thumbnail_hover_data_list.append(tuple(item))

thumbnail_hover_temp.putdata(thumbnail_hover_data_list)
#thumbnail_hover_temp.save(thumbnail_hover_path)
thumbnail_hover_corner_rounded = add_corners(thumbnail_hover_temp,6)
thumbnail_hover_corner_rounded.save(thumbnail_hover_path)
thumbnail_hover = tkinter.PhotoImage(file = thumbnail_hover_path)
return thumbnail_hover


def getThumbnail(video):
thumbnail_url = video.thumbnail_url
thumbnail_download_path = getValidFileName("./temp/" + removeInvalidChars(thumbnail_url) + ".png")
request.urlretrieve(thumbnail_url, thumbnail_download_path)

thumbnail_temp = Image.open(thumbnail_download_path)
#print(thumbnail_temp.width)
#print(thumbnail_temp.height)
if round(thumbnail_temp.width/4*3) <= 480:
thumbnail_temp = thumbnail_temp.resize((113, 64),Image.Resampling.LANCZOS).crop((0,8,113,56)).resize((113,64))
else:
thumbnail_temp = thumbnail_temp.resize((113,64),Image.Resampling.LANCZOS)#.crop((0,7,110,55))
#else:
# thumbnail_temp = thumbnail_temp.resize((103, 58),Image.Resampling.LANCZOS).crop((0,7,110,55)).resize((97,55))
thumbnail_temp_corner_rounded = add_corners(thumbnail_temp,6)
thumbnail_temp_corner_rounded.save(thumbnail_download_path)
thumbnail = tkinter.PhotoImage(file=thumbnail_download_path)
return thumbnail, getHoverThumbnail(thumbnail_download_path)


def getThumbnails(video):
return getThumbnail(video)
11 changes: 11 additions & 0 deletions functions/getValidFileName.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
import os

def getValidFileName(path):
splited = path.split(".")
file_name ,extenstion = ".".join(splited[0:-1]),splited[-1]
generate_file_name = file_name
i = 2
while os.path.exists(generate_file_name+"."+extenstion):
generate_file_name = file_name + " ({})".format(i)
i += 1
return generate_file_name + "." + extenstion
4 changes: 4 additions & 0 deletions functions/getWindowScale.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
import customtkinter as ctk

def getWindowScale(widget):
return 1/ctk.ScalingTracker.get_widget_scaling(widget)
2 changes: 2 additions & 0 deletions functions/passIt.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
def passIt():
pass
6 changes: 6 additions & 0 deletions functions/removeInvalidCharts.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
def removeInvalidChars(url:str):
filename = url
replaces = ["\\","/",":",'"',"?","<",">","|","*"]
for re in replaces:
filename = filename.replace(re,"~")
return filename
6 changes: 6 additions & 0 deletions functions/saveSettings.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
import json

def saveSettings(file, settings):
file = open(file, "w")
json.dump(obj=settings, fp=file, indent=8, sort_keys=True)
file.close()
31 changes: 31 additions & 0 deletions functions/sortDict.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
def sortDict(info):
video_keys = []
audio_keys = []
for data in (info):
key = (list(data.keys())[0])
if "kbps" in key:
audio_keys.append(key)
else:
video_keys.append(key)

for i in range(len(video_keys)):
for i2 in range(len(video_keys)-1):
if (int(video_keys[i2][:-1]) < int(video_keys[i2+1][:-1])):
video_keys[i2], video_keys[i2+1] = video_keys[i2+1], video_keys[i2]

keys = video_keys+audio_keys
sorted_dict: list = []
index2 = 0
break_ = False
while True:
for dict_ in info :
if list(dict_.keys())[0] == keys[index2]:
sorted_dict.append(dict_)
index2 += 1
if index2>=len(keys):
break_ = True
break
if break_:
break

return sorted_dict
Loading

0 comments on commit 77f887a

Please sign in to comment.