A YouTube Downloader GUI program thing

in #youtubedownloader29 days ago
import os
import concurrent.futures
from tkinter import *
from tkinter import filedialog, messagebox
from pytube import YouTube
import threading
import json
import logging
import re
from subprocess import call

queue = []
download_count = 0
max_downloads = 25
download_thread = None
downloaded_videos = set()
status_labels = []

logging.basicConfig(filename='app.log', filemode='w', format='%(asctime)s - %(message)s', level=logging.INFO)

def convert_to_mp3_ffmpeg(video_path, target_path):
    command = ["ffmpeg", "-i", video_path, target_path]
    call(command)
    logging.info(f"Converted video to MP3 at {target_path}")

def update_status(status_label, message):
    status_label.config(text=message)
    root.update_idletasks()

def download_video(url, path, status_label):
    global download_count
    try:
        update_status(status_label, "Downloading...")
        yt = YouTube(url)
        video = yt.streams.filter(only_audio=True).first()
        video_path = video.download(output_path=path)
        target_path = os.path.join(path, yt.title + '.mp3')
        convert_to_mp3_ffmpeg(video_path, target_path)
        os.remove(video_path)
        logging.info(f"Downloaded and converted video to MP3 at {target_path}")
        video_id = extract_video_id(url)
        downloaded_videos.add(video_id)
        download_count += 1
        update_status(status_label, "Download Complete")
    except Exception as e:
        logging.error(f"Error downloading {url}: {e}")
        update_status(status_label, "Download Failed")

def process_url(url_suffix, path):
    base_url = "https://www.youtube.com"
    full_url = base_url + url_suffix
    queue.append((full_url, path))

def start_downloads():
    global download_thread
    while queue:
        url, path = queue.pop(0)
        status_label = status_labels[download_count % len(status_labels)]
        download_video(url, path, status_label)
    download_thread = None

def search_related_videos():
    for video_id in downloaded_videos:
        search_results = YoutubeSearch(video_id, max_results=5).to_dict()
        for result in search_results:
            print(f"Related video: {result['title']} - URL: https://www.youtube.com{result['url_suffix']}")

def submit_download(event=None):
    global download_thread
    url = url_entry.get()
    path = path_entry.get()
    process_url(url, path)
    url_entry.delete(0, END)
    if not download_thread:
        download_thread = threading.Thread(target=start_downloads)
        download_thread.start()
    with open('config.json', 'w') as f:
        json.dump({'path': path}, f)

def extract_video_id(url):
    video_id_match = re.search(r"(?<=v=)[^&#]+", url)
    return video_id_match.group() if video_id_match else None

def load_path():
    if not os.path.exists('config.json'):
        with open('config.json', 'w') as f:
            json.dump({}, f)
    with open('config.json', 'r') as f:
        config = json.load(f)
        return config.get('path', '')

def set_max_downloads(val):
    global max_downloads
    max_downloads = int(val)

def choose_path():
    path = filedialog.askdirectory()
    path_entry.delete(0, END)
    path_entry.insert(0, path)

root = Tk()
root.geometry("1280x720")
bg_color = '#000000'
text_color = '#FFFFFF'
button_color = '#2D2D2D'
highlight_color = '#505050'
root.configure(background=bg_color)

url_label = Label(root, text="Enter URL:", bg=bg_color, fg=text_color)
url_label.pack()
url_entry = Entry(root, bg=bg_color, fg=text_color)
url_entry.bind('<Return>', submit_download)
url_entry.pack()

path_label = Label(root, text="Enter Path:", bg=bg_color, fg=text_color)
path_label.pack()
path_entry = Entry(root, bg=bg_color, fg=text_color)
path_entry.insert(0, load_path())
path_entry.pack()

choose_path_button = Button(root, text="Choose Path", command=choose_path, bg=button_color, activebackground=highlight_color)
choose_path_button.pack()

submit_button = Button(root, text="Download", command=submit_download, bg=button_color, activebackground=highlight_color)
submit_button.pack()

search_button = Button(root, text="Search from Past Downloads", command=search_related_videos, bg=button_color, activebackground=highlight_color)
search_button.pack()

max_downloads_slider = Scale(root, from_=5, to=25, orient=HORIZONTAL, label='Max Downloads', command=set_max_downloads)
max_downloads_slider.set(25)
max_downloads_slider.pack()

# Status labels for visual feedback
for i in range(3):
    status_label = Label(root, text="Waiting...", bg=bg_color, fg=text_color)
    status_label.pack()
    status_labels.append(status_label)

root.mainloop()

i made this program a while back .

its a youtube downloading system that can download YT stuff with a GUI nice and tidy. Can be a great too for journalists or power users who need a lot of videos.