-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
0 parents
commit b4eacb6
Showing
8 changed files
with
407 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,18 @@ | ||
#environment | ||
.venv/ | ||
|
||
# Byte-compiled / optimized / DLL files | ||
__pycache__/ | ||
|
||
# Distribution / packaging | ||
build/ | ||
dist/ | ||
downloads/ | ||
|
||
# PyInstaller | ||
*.manifest | ||
*.spec | ||
|
||
# Installer logs | ||
pip-log.txt | ||
pip-delete-this-directory.txt |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,67 @@ | ||
# YouTube Video Downloader 2.0 | ||
|
||
Effortlessly download YouTube videos in the highest quality with our user-friendly YouTube Video Downloader. Whether you need the video, audio, or both, our tool handles it all, directly saving files to your chosen folder for easy access and convenience. | ||
|
||
## Important Note | ||
|
||
**Users should have downloaded FFmpeg on their PC because `yt_dlp` needs it to function properly.** | ||
|
||
### How to Install FFmpeg | ||
|
||
1. Download FFmpeg from the official website: [FFmpeg.org](https://ffmpeg.org/download.html). | ||
2. Follow the installation instructions for your operating system. | ||
3. Make sure FFmpeg is added to your system's PATH. | ||
|
||
## Features | ||
|
||
- Download videos in the highest quality. | ||
- Option to download only audio or video. | ||
- Save files directly to your specified folder. | ||
|
||
## Requirements | ||
|
||
- Python 3.6 or higher | ||
- `yt_dlp` module | ||
- FFmpeg (ensure it is installed and available in your system's PATH) | ||
|
||
## Installation | ||
|
||
1. Clone the repository: | ||
```bash | ||
git clone https://github.com/darshitlimbad/YT_Video_Downloader.git | ||
``` | ||
2. Navigate to the project directory: | ||
```bash | ||
cd YT_Video_Downloader | ||
``` | ||
3. Install the required Python packages: | ||
```bash | ||
pip install -r requirements.txt | ||
``` | ||
|
||
## Usage | ||
|
||
1. Run the script: | ||
```bash | ||
python main.py | ||
``` | ||
2. Follow the on-screen instructions to download your desired video. | ||
|
||
## Building the Executable | ||
|
||
To build the executable file using cxfreeze, follow these steps: | ||
|
||
1. Install cxfreeze: | ||
```bash | ||
pip install cxfreeze | ||
``` | ||
2. Generate the executable: | ||
```bash | ||
cxfreeze main.py --build_exe dist/ --icon app.ico | ||
``` | ||
|
||
The executable will be available in the `dist` folder. | ||
|
||
## License | ||
|
||
IDK |
Binary file not shown.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,31 @@ | ||
import yt_dlp | ||
|
||
from module.download import * | ||
from module.formatting import print_colored_text | ||
|
||
def main(): | ||
print("-" * 100) | ||
|
||
# Display options to the user | ||
print("Please choose an option for download:") | ||
print("1. Playlist") | ||
print("2. Video") | ||
|
||
option = input("Enter your choice (1 or 2): ").strip() | ||
|
||
# Execute based on user's choice | ||
if option == "1": | ||
print("-" * 100) | ||
print_colored_text(text="Enter Playlist URL:", fg_color="green") | ||
playlist_url = input() | ||
download_playlist(playlist_url) | ||
elif option == "2": | ||
print("-" * 100) | ||
print_colored_text(text="Enter Video URL:", fg_color="green") | ||
video_url = input() | ||
download_video(video_url) | ||
else: | ||
print_colored_text(text="Invalid choice! Please enter 1 or 2.", fg_color="red") | ||
|
||
if __name__ == '__main__': | ||
main() |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,124 @@ | ||
import os | ||
import yt_dlp | ||
|
||
from .utils import * | ||
from .formatting import * | ||
|
||
def download_video_media(video_url, download_folder, video_format, audio_format): | ||
ydl_options = { | ||
'format': f'{video_format}+{audio_format}', | ||
'outtmpl': os.path.join(download_folder, '%(title)s.%(ext)s'), | ||
'progress_hooks': [hook_function], | ||
} | ||
|
||
with yt_dlp.YoutubeDL(ydl_options) as ydl: | ||
ydl.download([video_url]) | ||
|
||
def download_video(video_url,download_folder:str=".\\Downloads"): | ||
try: | ||
if '/playlist?' in video_url: | ||
download_playlist(video_url) | ||
return | ||
|
||
if not os.path.exists(download_folder): | ||
os.mkdir(download_folder) | ||
|
||
video_only_formats= {} | ||
audio_only_formats= {} | ||
|
||
# Get video info | ||
ydl_opts = { | ||
'quiet': True, | ||
'listformats': False, | ||
'noplaylist': True, | ||
} | ||
with yt_dlp.YoutubeDL(ydl_opts) as ydl: | ||
video_info = ydl.extract_info(video_url, download=False) | ||
|
||
print(f"\nProcessing: {video_info['title']}") | ||
|
||
formats = video_info.get('formats', []) | ||
|
||
video_only_formats = [f for f in formats if f.get('vcodec','none') != 'none' and f.get('acodec','none') == 'none'] | ||
audio_only_formats = [f for f in formats if f.get('vcodec','none') == 'none' and f.get('acodec','none') != 'none'] | ||
|
||
if not len(video_only_formats) or not len(audio_only_formats): | ||
print_colored_text("No Format found\n",fg_color="red") | ||
return | ||
|
||
# Validate and get user input for the desired video and audio | ||
print_formats(format_type="video",video_only_formats=video_only_formats) | ||
print_colored_text(text="Enter Video Index:",fg_color="green") | ||
video_index = get_valid_index("", len(video_only_formats)) | ||
|
||
print_formats(format_type="audio",audio_only_formats=audio_only_formats) | ||
print_colored_text(text="Enter Audio Index: ",fg_color="green") | ||
audio_index = get_valid_index("", len(audio_only_formats)) | ||
|
||
# Retrieve the selected format IDs | ||
video_format = video_only_formats[video_index]['format_id'] | ||
audio_format = audio_only_formats[audio_index]['format_id'] | ||
|
||
if not video_format or not audio_format: | ||
print_colored_text("No Format found\n",fg_color="red") | ||
return | ||
|
||
# Download the video with yt-dlp, which will automatically merge video and audio | ||
download_video_media(video_url, download_folder, video_format, audio_format) | ||
|
||
except Exception as e: | ||
print_colored_text("\nSomething went wrong:",fg_color="red") | ||
print(e) | ||
|
||
def download_playlist(playlist_url): | ||
try: | ||
if '/watch?' in playlist_url: | ||
download_video(playlist_url) | ||
return | ||
|
||
download_root = '.\\Downloads' | ||
if not os.path.exists(download_root): | ||
os.mkdir(download_root) | ||
|
||
# Get playlist info | ||
ydl_opts = { | ||
'quiet': True, | ||
'listformats': False, | ||
} | ||
|
||
with yt_dlp.YoutubeDL(ydl_opts) as ydl: | ||
playlist_info = ydl.extract_info(playlist_url, download=False) | ||
|
||
playlist_title = playlist_info.get('title', 'Playlist') | ||
playlist_folder = os.path.join(download_root, playlist_title) | ||
|
||
if not os.path.exists(playlist_folder): | ||
os.mkdir(playlist_folder) | ||
|
||
print_colored_text(text="\nDownloading Playlist: ",fg_color="green") | ||
print(playlist_title) | ||
print_colored_text(text="Saving files to:",fg_color="green") | ||
print(playlist_folder,end="\n\n") | ||
|
||
for entry in playlist_info['entries']: | ||
video_url = entry['webpage_url'] | ||
download_video(video_url,download_folder=playlist_folder) | ||
|
||
print_colored_text(text="\nPlaylist download successfully completed.",fg_color="green") | ||
print_colored_text(text="\nPlaylist folder:",fg_color="green") | ||
print(playlist_folder,end="\n\n") | ||
print("-"*100) | ||
|
||
|
||
except KeyError as ke: | ||
print_colored_text("\nProcess Terminated. \nIt looks like you entered a single video URL instead of a playlist URL.",fg_color="red") | ||
|
||
if os.path.exists(playlist_folder) and len(os.listdir(playlist_folder)) == 0: | ||
os.rmdir(playlist_folder) | ||
except Exception as e: | ||
print_colored_text("\nSomething went wrong:",fg_color="red") | ||
print(e) | ||
|
||
if os.path.exists(playlist_folder) and len(os.listdir(playlist_folder)) == 0: | ||
os.rmdir(playlist_folder) | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,100 @@ | ||
from .utils import get_file_size_from_url , convert_bytes | ||
|
||
# ascii escape codes | ||
esc_codes = { | ||
# color codes | ||
'fore': { | ||
'red': '\033[31m', # Red text | ||
'green': '\033[32m', # Green text | ||
'bright_red': '\033[91m', # Bright Red text | ||
'bright_green': '\033[92m', # Bright Green text | ||
'dark_grey': '\033[90m', # Dark Grey text | ||
'reset': '\033[39m', # Reset to default foreground | ||
}, | ||
'back': { | ||
'red': '\033[41m', # Red background | ||
'green': '\033[42m', # Green background | ||
'light_red': '\033[101m', # Light Red background | ||
'light_green': '\033[102m', # Light Green background | ||
'light_yellow': '\033[103m', # Light Yellow background | ||
'light_blue': '\033[104m', # Light Blue background | ||
'light_magenta': '\033[105m', # Light Magenta background | ||
'light_cyan': '\033[106m', # Light Cyan background | ||
'light_white': '\033[107m', # Light White background | ||
'reset': '\033[49m', # Reset to default background | ||
|
||
}, | ||
|
||
'bold': '\033[1m', # Bold text attribute | ||
'underline': '\033[4m', # Underlined text attribute | ||
|
||
'reset': '\033[0m', # Reset all attributes | ||
} | ||
|
||
|
||
def print_formats(format_type:str="video", video_only_formats:dict =None, audio_only_formats:dict =None): | ||
""" | ||
Prints available formats in a structured table format based on the format type. | ||
Args: | ||
format_type (str): Type of formats to print, either 'video' or 'audio'. | ||
video_only_formats (list, optional): A list of dictionaries containing video format information. | ||
audio_only_formats (list, optional): A list of dictionaries containing audio format information. | ||
Description: | ||
This function displays the available formats (video or audio) in a tabular format. | ||
It includes headers for each column and prints a list of formats with their details. | ||
Each format is displayed with an index, which allows users to choose a format based | ||
on its number. | ||
""" | ||
|
||
if format_type == 'video': | ||
header = f"{f'{esc_codes['fore']['bright_green']}Index{esc_codes['reset']}':<5} {'Format ID':<15} {'Codec':<15} {'Resolution':<20} {'FPS':<10} {'Filesize':<20}" | ||
formats = video_only_formats | ||
fields = ['format_id', 'vcodec', 'format_note', 'fps', 'filesize'] | ||
elif format_type == 'audio': | ||
header = f"{f'{esc_codes['fore']['bright_green']}Index{esc_codes['reset']}{esc_codes['bold']}':<5} {'Format ID':<15} {'Codec':<15} {'Quality':<20} {'Filesize':<20}" | ||
formats = audio_only_formats | ||
fields = ['format_id', 'acodec', 'format_note', 'filesize'] | ||
else: | ||
raise ValueError("Invalid format type. Choose 'video' or 'audio'.") | ||
|
||
heading = f"\n{esc_codes['bold']}{esc_codes['fore']['green']}Available {format_type} formats:{esc_codes['reset']}" | ||
print(heading) | ||
print("=" * len(header)) # Separator line | ||
print(f'{esc_codes['bold']}{header}{esc_codes['reset']}') # Print the formats header | ||
print("=" * len(header)) # Separator line | ||
|
||
for i, f in enumerate(formats, start=1): | ||
|
||
filesize = convert_bytes(f.get('filesize')) or f"{esc_codes['fore']['dark_grey']} ~ {convert_bytes(get_file_size_from_url(f.get('url')))} ( dash ) {esc_codes['reset']}" | ||
|
||
|
||
# Define the common elements for both video and audio | ||
base_format = (f"{esc_codes['fore']['green']}{esc_codes['bold']}{i:<5}{esc_codes['reset']} " | ||
f"{f.get(fields[0], 'N/A'):<15} {f.get(fields[1], 'N/A'):<15} {f.get(fields[2], 'N/A'):<20}") | ||
|
||
# Add specific details based on the format type | ||
if format_type == 'video': | ||
print(f"{base_format} {f.get(fields[3], 'N/A'):<10} {filesize:<20}") | ||
elif format_type == 'audio': | ||
print(f"{base_format} {filesize:<20}") | ||
|
||
print("=" * len(header)) | ||
|
||
def print_colored_text(text, fg_color=None, bg_color=None): | ||
""" | ||
Prints text with specified foreground and background colors, with an option to reset color attributes. | ||
Args: | ||
text (str): The text to print. | ||
fg_color (str, optional): The foreground color. Choices: 'red', 'green', 'bright_red', 'bright_green', etc. | ||
bg_color (str, optional): The background color. Choices: 'light_red', 'light_green', etc. | ||
reset (str, optional): The type of reset. Choices: 'all', 'foreground', 'background'. | ||
""" | ||
fg_code = esc_codes['fore'].get(fg_color, '') | ||
bg_code = esc_codes['back'].get(bg_color, '') | ||
reset_code = esc_codes['reset'] | ||
|
||
# Print text with color codes | ||
print(f"{fg_code}{bg_code}{text}{reset_code}",end="") |
Oops, something went wrong.