Last active
February 9, 2025 10:10
-
-
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
This file contains hidden or 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
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