From aea4434b6db515d32b7ab1a6c65a0674ace219bf Mon Sep 17 00:00:00 2001 From: sulubsquared Date: Sun, 22 Dec 2024 23:27:18 -0500 Subject: [PATCH] mp4 support; thumbnail preview --- .gitignore | 16 +++++++ main.py | 124 ++++++++++++++++++++++++++++++++++++++++++++++++++++- 2 files changed, 139 insertions(+), 1 deletion(-) create mode 100644 .gitignore diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..33660fc --- /dev/null +++ b/.gitignore @@ -0,0 +1,16 @@ +# Virtual Environment +.venv/ +venv/ + +# PyInstaller files +*.spec +dist/ +build/ + +# Python cache +__pycache__/ +*.pyc + +# IDE settings +.vscode/ +.idea/ \ No newline at end of file diff --git a/main.py b/main.py index f0b595b..d8ee219 100644 --- a/main.py +++ b/main.py @@ -4,10 +4,14 @@ import time from datetime import timedelta import humanize +from PIL import Image, ImageTk +import requests +from io import BytesIO def format_size(bytes): return humanize.naturalsize(bytes, binary=True) +# mp3 init def beginDownload(): # show prog bar when button pressed progressBar.pack(padx=10, pady=10) @@ -88,6 +92,12 @@ def progress_hook(d): 'progress_hooks': [progress_hook], } + # fetch metadata for thumbnail + with yt_dlp.YoutubeDL({'quiet': True}) as ydl: + info = ydl.extract_info(ytLink, download=False) + thumbnail_url = info.get('thumbnail', '') + display_thumbnail(thumbnail_url) + with yt_dlp.YoutubeDL(ydl_opts) as ydl: finishLabel.configure(text="Starting download...", text_color="white") ydl.download([ytLink]) @@ -100,6 +110,112 @@ def progress_hook(d): progPercentage.pack_forget() downloadStats.pack_forget() +# mp4 init +def beginDownloadMP4(): + # show progress bar when button is pressed + progressBar.pack(padx=10, pady=10) + progPercentage.pack() + downloadStats.pack() + + start_time = time.time() + try: + ytLink = link.get() + if not ytLink.strip(): + raise ValueError("No link provided. Please enter a valid YouTube link.") + + def progress_hook(d): + if d['status'] == 'downloading': + try: + downloaded = d.get('downloaded_bytes', 0) + total = d.get('total_bytes', 0) or d.get('total_bytes_estimate', 0) + speed = d.get('speed', 0) + + if total and downloaded: + progress = min(downloaded / total, 1.0) + app.after(10, lambda: progressBar.set(progress)) + + if speed > 0: + eta = (total - downloaded) / speed + eta_str = str(timedelta(seconds=int(eta))) + else: + eta_str = "calculating..." + + percent = f"{int(progress * 100)}%" + speed_str = f"{format_size(speed)}/s" if speed else "calculating..." + size_str = f"{format_size(downloaded)} / {format_size(total)}" + elapsed = time.time() - start_time + elapsed_str = str(timedelta(seconds=int(elapsed))) + + app.after(10, lambda: progPercentage.configure(text=percent)) + app.after(10, lambda: downloadStats.configure( + text=f"Speed: {speed_str}\n" + f"Size: {size_str}\n" + f"Elapsed Time: {elapsed_str}\n" + f"ETA: {eta_str}" + )) + app.update() + except Exception as e: + print(f"Progress update error: {e}") + pass + + elif d['status'] == 'finished': + try: + total_time = time.time() - start_time + file_size = os.path.getsize(d['filename']) + + app.after(10, lambda: finishLabel.configure( + text="Download Complete!", + text_color="green" + )) + app.after(10, lambda: downloadStats.configure( + text=f"Completed in: {str(timedelta(seconds=int(total_time)))}\n" + f"Final Size: {format_size(file_size)}" + )) + app.after(10, lambda: progressBar.set(1)) + app.update() + except Exception as e: + print(f"Completion update error: {e}") + finishLabel.configure(text="Download Complete!", text_color="green") + + ydl_opts = { + 'format': 'bestvideo+bestaudio/best', + 'outtmpl': '%(title)s.%(ext)s', + 'merge_output_format': 'mp4', + 'progress_hooks': [progress_hook], + } + + # fetch metadata for thumbnail + with yt_dlp.YoutubeDL({'quiet': True}) as ydl: + info = ydl.extract_info(ytLink, download=False) + thumbnail_url = info.get('thumbnail', '') + display_thumbnail(thumbnail_url) + + with yt_dlp.YoutubeDL(ydl_opts) as ydl: + finishLabel.configure(text="Starting download...", text_color="white") + ydl.download([ytLink]) + + except Exception as e: + print(f"Error: {e}") + finishLabel.configure(text="Error: Invalid Link or Connection Issue!", text_color="red") + progressBar.pack_forget() + progPercentage.pack_forget() + downloadStats.pack_forget() + +# function to display thumbnail +def display_thumbnail(url): + try: + response = requests.get(url) + response.raise_for_status() + image_data = Image.open(BytesIO(response.content)) + image_data.thumbnail((200, 200)) # resize + thumbnail_image = ImageTk.PhotoImage(image_data) + thumbnail_label.configure(image=thumbnail_image) + thumbnail_label.image = thumbnail_image + thumbnail_label.pack(pady=10) + except Exception as e: + print(f"Error displaying thumbnail: {e}") + thumbnail_label.pack_forget() + # sys settings customtkinter.set_appearance_mode("System") customtkinter.set_default_color_theme("blue") @@ -126,14 +242,20 @@ def progress_hook(d): finishLabel = customtkinter.CTkLabel(app, text="") finishLabel.pack(pady=5) +thumbnail_label = customtkinter.CTkLabel(app) +thumbnail_label.pack_forget() + # prog bar hidden by default progPercentage = customtkinter.CTkLabel(app, text="0%") downloadStats = customtkinter.CTkLabel(app, text="") + progressBar = customtkinter.CTkProgressBar(app, width=400) progressBar.set(0) download = customtkinter.CTkButton(app, text="Download as MP3", command=beginDownload) +downloadmp4 = customtkinter.CTkButton(app, text="Download as MP4", command=beginDownloadMP4) download.pack(padx=25, pady=15) +downloadmp4.pack(padx=25, pady=15) # run apps -app.mainloop() \ No newline at end of file +app.mainloop()