GG_Y-Downloader/GG_Y-Downloader.py

273 lines
9.6 KiB
Python

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()