Skip to content

Instantly share code, notes, and snippets.

@SahandMalaei
Last active February 22, 2025 08:29
Show Gist options
  • Select an option

  • Save SahandMalaei/6f74adc57f8c5a7fdf963f349c3ad3f4 to your computer and use it in GitHub Desktop.

Select an option

Save SahandMalaei/6f74adc57f8c5a7fdf963f349c3ad3f4 to your computer and use it in GitHub Desktop.
Python script to download videos from YouTube
import tkinter as tk
from tkinter import filedialog
import yt_dlp
import ffmpeg
import os
# Mapping resolutions based on the larger dimension (either width or height)
RESOLUTION_CATEGORIES = {
"2160p": 3840, # 2160p for 3840+ width or height
"1440p": 2560, # 1440p for 2560+ width or height
"1080p": 1920, # 1080p for 1920+ width or height
"720p": 1280, # 720p for 1280+ width or height
"480p": 854 # 480p for 854+ width or height
}
def categorize_resolution(width, height):
"""Categorizes a resolution into a standard group (e.g., 1080p) based on the larger dimension."""
max_dimension = max(width, height)
for category, min_dimension in RESOLUTION_CATEGORIES.items():
if max_dimension >= min_dimension:
return category
return None # Ignore resolutions that don’t fit
def download_video():
url = input("Enter YouTube video URL: ")
ydl_opts = {
"quiet": True,
"listformats": True
}
with yt_dlp.YoutubeDL(ydl_opts) as ydl:
info = ydl.extract_info(url, download=False)
# Organizing video formats
video_formats = {}
for fmt in info.get("formats", []):
width, height = fmt.get("width", 0), fmt.get("height", 0)
fps = fmt.get("fps", 0) # Frames per second
bitrate = fmt.get("tbr", 0) # Total bitrate
if width and height:
matched_res = categorize_resolution(width, height)
if matched_res and fmt.get("vcodec") != "none" and fmt.get("acodec") == "none":
# Select the highest FPS and bitrate available for each resolution category
if matched_res not in video_formats or (fps > video_formats[matched_res]["fps"]) or (fps == video_formats[matched_res]["fps"] and bitrate > video_formats[matched_res]["bitrate"]):
video_formats[matched_res] = {"format_id": fmt["format_id"], "fps": fps, "bitrate": bitrate}
# Show available resolutions
available_resolutions = sorted(video_formats.keys(), reverse=True)
if not available_resolutions:
print("No suitable video formats found.")
return
print("\nAvailable video resolutions:")
for i, res in enumerate(available_resolutions):
print(f"{i + 1}. {res} (Best FPS & Quality Available)")
choice = int(input("\nEnter the number of the desired video quality: ")) - 1
selected_resolution = available_resolutions[choice]
selected_video_format = video_formats[selected_resolution]["format_id"]
print (f"Selected stream ID: {selected_video_format}")
# Find highest quality audio format
best_audio_format = None
best_audio_bitrate = 0
for fmt in info.get("formats", []):
if fmt.get("acodec") != "none" and fmt.get("vcodec") == "none":
bitrate = fmt.get("abr", 0) # Ensure None is treated as 0
if bitrate and bitrate > best_audio_bitrate:
best_audio_bitrate = bitrate
best_audio_format = fmt["format_id"]
if not best_audio_format:
print("No suitable audio format found.")
return
# File dialog for saving
root = tk.Tk()
root.withdraw()
root.attributes('-topmost', True) # Bring to front
root.update() # Update window state
save_path = filedialog.asksaveasfilename(defaultextension=".mp4", filetypes=[("MP4 files", "*.mp4")], parent=root)
if not save_path:
print("Download canceled.")
return
temp_video = save_path + "_video.mp4"
temp_audio = save_path + "_audio.mp4"
# Download video and audio separately
print(f"Downloading video at {selected_resolution} (Highest FPS & Quality)...")
ydl_opts_video = {"format": selected_video_format, "outtmpl": temp_video}
with yt_dlp.YoutubeDL(ydl_opts_video) as ydl:
ydl.download([url])
print("Downloading highest quality audio...")
ydl_opts_audio = {"format": best_audio_format, "outtmpl": temp_audio}
with yt_dlp.YoutubeDL(ydl_opts_audio) as ydl:
ydl.download([url])
# Merge video and audio using FFmpeg with multiple threads
print("Merging video and audio (using multiple CPU threads)...")
(
ffmpeg
.input(temp_video)
.output(ffmpeg.input(temp_audio), save_path, vcodec="copy", acodec="aac", threads=0) # Auto-detect CPU threads
.run(overwrite_output=True)
)
# Clean up temporary files
os.remove(temp_video)
os.remove(temp_audio)
print("Download complete! Video saved at:", save_path)
if __name__ == "__main__":
download_video()
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment