Skip to content

Instantly share code, notes, and snippets.

@werdl
Last active February 9, 2025 10:10
Show Gist options
  • Save werdl/fe775ebf92941ee73562dd85e5de92f1 to your computer and use it in GitHub Desktop.
Save werdl/fe775ebf92941ee73562dd85e5de92f1 to your computer and use it in GitHub Desktop.
yt-dlp GUI (eg. where command line inaccesible/inconvenient) Dependencies: yt-dlp, tkinter
import yt_dlp
import tkinter as tk
from tkinter import scrolledtext
import threading
import json
def load_preferences():
try:
with open('preferences.json', 'r') as f:
return json.load(f)
except FileNotFoundError:
return {}
def download_video(url, info_text, progress_hook, outname):
ydl_opts = {
'outtmpl': f'{outname}%(title)s.%(ext)s',
'progress_hooks': [progress_hook],
'prefer_ffmpeg': True,
}
try:
with yt_dlp.YoutubeDL(ydl_opts) as ydl:
info = ydl.extract_info(url)
return info
except Exception as e:
info_text.insert(tk.END, f"Error: {e}\n")
return None
class App(tk.Frame):
def __init__(self, master, preferences={}):
super().__init__(master)
self.pack(expand=True, fill=tk.BOTH)
self.contents = tk.StringVar()
self.placeholder = "Enter URL..."
self.contents.set(self.placeholder)
self.entry = tk.Entry(self, textvariable=self.contents, font=("Consolas", 14))
self.entry.pack(pady=(20, 10), fill=tk.X, padx=10)
self.entry.bind("<FocusIn>", self.on_focus_in)
self.entry.bind("<FocusOut>", self.on_focus_out)
self.entry.bind('<Return>', self.download_wrapper)
self.info_text = scrolledtext.ScrolledText(self, wrap=tk.WORD, height=15, font=("Consolas", 10))
self.info_text.pack(pady=(0, 10), fill=tk.BOTH, expand=True, padx=10)
root.resizable(True, True)
self.master = master
self.outname = preferences.get('outname', '..\..\..\Downloads\YOUTUBE\\')
def on_focus_in(self, event):
if self.contents.get() == self.placeholder:
self.contents.set("")
def on_focus_out(self, event):
if self.contents.get() == "":
self.contents.set(self.placeholder)
def download_wrapper(self, event):
url = self.contents.get()
if url != self.placeholder:
self.info_text.insert(tk.END, f"Downloading f{url}...\n")
self.info_text.see(tk.END)
def progress_hook(d):
if 'downloaded_bytes' in d and 'total_bytes' in d:
to_out = round((d['downloaded_bytes'] / d['total_bytes']) * 100, 1)
self.info_text.insert(tk.END, f"{to_out}% downloaded ({d['downloaded_bytes']}B/{d['total_bytes']}B)\n")
self.info_text.see(tk.END)
download_thread = threading.Thread(target=self.run_download, args=(url, progress_hook, self.outname))
download_thread.start()
def run_download(self, url, progress_hook, outname):
download_info = download_video(url, self.info_text, progress_hook, outname)
if download_info:
title = download_info.get('title', 'Unknown Title')
self.info_text.insert(tk.END, f"Title: {title}\n")
self.info_text.insert(tk.END, f"Info: {download_info.get('format', 'Unknown')}\n")
self.info_text.see(tk.END)
def update_output(self, output):
for line in output:
self.info_text.insert(tk.END, line + "\n")
self.info_text.see(tk.END)
root = tk.Tk()
root.title("YouTube Downloader")
try:
try:
icon = tk.PhotoImage(file="youtube.png")
root.iconphoto(True, icon)
except tk.TclError:
print("Icon not found. Continuing without icon.")
except FileNotFoundError:
print("Icon not found. Continuing without icon.")
screen_width = root.winfo_screenwidth()
screen_height = root.winfo_screenheight()
window_width = int(screen_width / 3)
window_height = int(screen_height / 3)
position_top = int(screen_height / 2 - window_height / 2)
position_right = int(screen_width / 2 - window_width / 2)
root.geometry(f'{window_width}x{window_height}+{position_right}+{position_top}')
root.bind('<Escape>', lambda e: root.quit())
root.bind('<Control-q>', lambda e: root.quit())
app = App(root, preferences=load_preferences())
root.mainloop()
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment