273 lines
9.6 KiB
Python
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()
|