import os import json import time import tkinter as tk from tkinter import messagebox, ttk, filedialog from threading import Thread from yt_dlp import YoutubeDL current_download = None download_thread = None cancel_download = False total_filesize = 0 url_cleared = False settings_file = "settings.json" fake_progress_thread = None fake_progress_running = False def create_download_directory(path=None): if path: download_path = path else: download_path = os.path.join(os.getcwd(), "Media-Youtube") if not os.path.exists(download_path): os.makedirs(download_path) return download_path def fake_progress(): global fake_progress_running, progress_var progress_steps = [3, 15, 25, 37, 50, 70] time_intervals = [7, 6, 10, 15, 20] wait_times = [2, 1, 1, 1, 1] fake_progress_running = True for step, interval, wait in zip(progress_steps, time_intervals, wait_times): if not fake_progress_running: break while progress_var.get() < step: progress_var.set(min(progress_var.get() + 1, step)) progress_label.config(text=f"{progress_var.get():.1f}%") root.update_idletasks() time.sleep(wait) time.sleep(interval) while fake_progress_running and progress_var.get() < 100: root.update_idletasks() time.sleep(0.5) def progress_hook(d): global cancel_download, fake_progress_running, total_filesize if cancel_download: fake_progress_running = False raise Exception("Download Aborted by User") if d['status'] == 'finished': progress_var.set(100) progress_label.config(text="100%") fake_progress_running = False def download_youtube_video(url, download_type): global cancel_download, total_filesize download_path = create_download_directory(selected_path.get()) ydl_opts = { 'outtmpl': os.path.join(download_path, '%(title)s.%(ext)s'), 'progress_hooks': [progress_hook], 'quiet': True, 'no_color': True } if download_type == "video": ydl_opts['format'] = 'bestvideo[ext=mp4]+bestaudio[ext=m4a]/best[ext=mp4]' elif download_type == "audio": ydl_opts['format'] = 'bestaudio' ydl_opts['postprocessors'] = [{ 'key': 'FFmpegExtractAudio', 'preferredcodec': 'mp3', 'preferredquality': '192', }] elif download_type == "both": ydl_opts['format'] = 'bestvideo[ext=mp4]+bestaudio[ext=m4a]/best[ext=mp4]' try: with YoutubeDL(ydl_opts) as ydl: ydl.download([url]) except Exception as e: if str(e) != "Download Aborted by User": messagebox.showerror("Error", f"Failed to download video: {e}") return False audio_opts = { 'format': 'bestaudio', 'outtmpl': os.path.join(download_path, '%(title)s-audio.mp3'), 'postprocessors': [{ 'key': 'FFmpegExtractAudio', 'preferredcodec': 'mp3', 'preferredquality': '192', }], 'quiet': True, 'no_color': True, 'progress_hooks': [progress_hook] } try: with YoutubeDL(audio_opts) as ydl_audio: ydl_audio.download([url]) return True except Exception as e: if str(e) != "Download Aborted by User": messagebox.showerror("Error", f"Failed to download audio: {e}") return False return True try: with YoutubeDL(ydl_opts) as ydl: ydl.download([url]) return True except Exception as e: if str(e) != "Download Aborted by User": messagebox.showerror("Error", f"Failed to download: {e}") return False def handle_download(): global download_thread, cancel_download, url_cleared, fake_progress_running url = url_entry.get() download_type = var.get() if not url: messagebox.showwarning("Input Error", "Please enter a valid YouTube URL.") return progress_var.set(0) progress_label.config(text="0%") cancel_download = False url_cleared = False global fake_progress_thread fake_progress_thread = Thread(target=fake_progress) fake_progress_thread.start() download_button.config(state=tk.DISABLED) cancel_button.config(state=tk.NORMAL) success = download_youtube_video(url, download_type) if success: fake_progress_running = False messagebox.showinfo("Download Complete", "Download completed successfully!") progress_var.set(100) progress_label.config(text="100%") add_to_download_list(url) download_button.config(state=tk.NORMAL) cancel_button.config(state=tk.DISABLED) fake_progress_running = False def start_download(): global download_thread download_thread = Thread(target=handle_download) download_thread.start() def cancel_download_func(): global cancel_download, fake_progress_running cancel_download = True fake_progress_running = False messagebox.showinfo("Cancel", "Download will be canceled shortly.") def clear_url_entry(event): global url_cleared if not url_cleared: url_entry.delete(0, tk.END) url_cleared = True def add_to_download_list(url): download_path = create_download_directory(selected_path.get()) ydl_opts = { 'quiet': True, 'skip_download': True } with YoutubeDL(ydl_opts) as ydl: info = ydl.extract_info(url, download=False) if download_list.size() >= 5: download_list.delete(0) download_list.insert(tk.END, info['title']) save_settings() def select_download_path(): path = filedialog.askdirectory() if path: selected_path.set(path) save_settings() def save_settings(): settings = { "download_path": selected_path.get(), "latest_downloads": download_list.get(0, tk.END) } with open(settings_file, 'w') as file: json.dump(settings, file) def load_settings(): if os.path.exists(settings_file): with open(settings_file, 'r') as file: settings = json.load(file) selected_path.set(settings.get("download_path", os.path.join(os.getcwd(), "Media-Youtube"))) for item in settings.get("latest_downloads", []): download_list.insert(tk.END, item) def style_widgets(): root.configure(bg='#2b2b2b') style = ttk.Style() style.theme_use('clam') style.configure('TButton', background='#3c3f41', foreground='#ffffff', relief='flat', font=('Helvetica', 12)) style.map('TButton', background=[('active', '#4b4f51'), ('hover', '#4caf50')]) style.configure('TLabel', background='#2b2b2b', foreground='#ffffff', font=('Helvetica', 14)) style.configure('TEntry', fieldbackground='#3c3f41', foreground='#ffffff', relief='flat') style.configure('TRadiobutton', background='#2b2b2b', foreground='#ffffff', font=('Helvetica', 12), indicatoron=0, width=20, padding=(5,5)) style.map('TRadiobutton', background=[('selected', '#005A99'), ('hover', '#3c3f41')], relief=[('pressed', 'sunken'), ('!pressed', 'flat')], foreground=[('selected', '#ffffff')]) style.configure('TFrame', background='#2b2b2b') style.configure('TListbox', background='#3c3f41', foreground='#ffffff') style.configure('TProgressbar', background='#4caf50', troughcolor='#3c3f41') style.configure('SmallLabel.TLabel', font=('Helvetica', 10), background='#2b2b2b', foreground='#ffffff') def start_gui(): global root, url_entry, var, progress_var, progress_label, download_button, cancel_button, download_list, selected_path root = tk.Tk() root.title("GG Y-Downloader") root.geometry("561x534") root.resizable(False, False) style_widgets() ttk.Label(root, text="YouTube URL:").pack(pady=10) url_entry = ttk.Entry(root, width=50) url_entry.pack(pady=5) url_entry.bind("<FocusIn>", clear_url_entry) var = tk.StringVar(value="video") ttk.Radiobutton(root, text="Video Download", variable=var, value="video").pack() ttk.Radiobutton(root, text="Audio Download", variable=var, value="audio").pack() ttk.Radiobutton(root, text="Video & Audio Download", variable=var, value="both").pack() selected_path = tk.StringVar(value=os.path.join(os.getcwd(), "Media-Youtube")) path_frame = ttk.Frame(root) ttk.Label(path_frame, text="Download Path:").pack(side=tk.LEFT, padx=5) ttk.Entry(path_frame, textvariable=selected_path, width=40).pack(side=tk.LEFT) ttk.Button(path_frame, text="Change", command=select_download_path).pack(side=tk.LEFT, padx=5) path_frame.pack(pady=10) progress_var = tk.DoubleVar() progress_bar = ttk.Progressbar(root, variable=progress_var, maximum=100) progress_bar.pack(pady=(20, 5), fill=tk.X, padx=10) progress_label = ttk.Label(root, text="0%", background='#2b2b2b', foreground='#ffffff', font=('Helvetica', 12)) progress_label.pack(pady=(0, 10)) download_button = ttk.Button(root, text="Download", command=start_download) download_button.pack(pady=5) cancel_button = ttk.Button(root, text="Cancel", command=cancel_download_func, state=tk.DISABLED) cancel_button.pack(pady=5) ttk.Label(root, text="Latest Downloads", style='SmallLabel.TLabel').pack(pady=(15, 0), anchor='w', padx=36) download_list = tk.Listbox(root, height=8, width=60, bg='#3c3f41', fg='#ffffff') download_list.pack(pady=5) load_settings() root.protocol("WM_DELETE_WINDOW", lambda: (save_settings(), root.destroy())) root.mainloop() if __name__ == "__main__": start_gui()