I'm starting in python and developing a youtube video/audio downloader for my wife. It is already functional, but I wanted to improve the look of it by placing a download progress bar, which can be determined or indeterminate. I've searched several places, even rolled over stackoverflow, but i couldn't find the solution for myself. Maybe my code's a little messed up, but I'm working on it.
As I did two separate programs, I will post only the video downloader. The audio contains a few more lines, just changing the type of file I want from the link and converting it to mp3, since the tkinter downloads in mp4 only audio. Then I'm going to integrate the two into one.
from pytube import YouTube
from tkinter import *
from threading import Thread
def threading():
t1 = Thread(target=downloader)
t1.start()
def threading2():
t1 = Thread(target=downloader2)
t1.start()
def threading3():
t1 = Thread(target=downloader3)
t1.start()
def threading4():
t1 = Thread(target=downloader4)
t1.start()
def threading5():
t1 = Thread(target=downloader5)
t1.start()
def downloader():
url = YouTube(str(inp.get()))
video = url.streams.get_highest_resolution()
video.download()
Label(janela, text='Baixado', font='arial 15').grid(column=0, row=9)
def downloader2():
url = YouTube(str(inp2.get()))
video = url.streams.get_highest_resolution()
video.download()
Label(janela, text='Baixado', font='arial 15').grid(column=0, row=9)
def downloader3():
url = YouTube(str(inp3.get()))
video = url.streams.get_highest_resolution()
video.download()
Label(janela, text='Baixado', font='arial 15').grid(column=0, row=9)
def downloader4():
url = YouTube(str(inp4.get()))
video = url.streams.get_highest_resolution()
video.download()
Label(janela, text='Baixado', font='arial 15').grid(column=0, row=9)
def downloader5():
url = YouTube(str(inp5.get()))
video = url.streams.get_highest_resolution()
video.download()
Label(janela, text='Baixado', font='arial 15').grid(column=0, row=9)
janela = Tk()
janela.title('God Of YouTube Downloader - No AD Version')
janela.geometry('480x500')
janela.resizable(0, 0)
janela.config(background='#dde')
inp = StringVar()
inp2 = StringVar()
inp3 = StringVar()
inp4 = StringVar()
inp5 = StringVar()
imgLogo = PhotoImage(file='youtube.gif')
imagem_logo = Label(janela, image=imgLogo).grid(column=0, row=0, pady=20)
cole_aqui = Label(janela, text='Cole aqui, seu link!', font='arial 15 bold', background='#dde')
cole_aqui.grid(column=0, row=1, pady=5)
entrada_download = Entry(janela, width=50, textvariable=inp)
entrada_download.grid(column=0, row=2, padx=82, pady=3)
entrada_download2 = Entry(janela, width=50, textvariable=inp2)
entrada_download2.grid(column=0, row=4, padx=82, pady=3)
entrada_download3 = Entry(janela, width=50, textvariable=inp3)
entrada_download3.grid(column=0, row=5, padx=82, pady=3)
entrada_download4 = Entry(janela, width=50, textvariable=inp4)
entrada_download4.grid(column=0, row=6, padx=82, pady=3)
entrada_download5 = Entry(janela, width=50, textvariable=inp5)
entrada_download5.grid(column=0, row=7, padx=82, pady=3)
botao_download = Button(janela, text='Baixar', width=20,
command=lambda: [threading(), threading2(), threading3(), threading4(), threading5()])
botao_download.grid(column=0, row=8, pady=20)
identif = Label(janela, text='@God Of Python', font='arial 8 bold').place(x=380, y=470)
janela.mainloop()
CodePudding user response:
I created example which uses Label
to display how many bytes remained to download.
If you get file length then you could calculate it as percentage.
YouTube()
can use on_progress_callback
and on_complete_callback
to execute functions during downloading and they can be used to display progress.
These functions runs in new threads and they can't update widgets directly (it generate error) so I use Queue()
to send values from these functions to main thread. And main thread uses root.after()
to execute update_status()
every 100ms. And this function gets information from queue and update labels.
Every thread gets number
to later send it back and update_status()
which label to update
And similar way it could work with widgets Progressbar.
For test I added few links directly in code - so I did't have to put them manually in every run.
Because I use for
-loops and list
so I reduced code but still have 5 Entries, and can download 5 files at the same time. If I would use range(10)
then I could download 10 files.
Originally I created all_threads
to keep all threads and use is_alive()
to check finished threads but now I don't use this list.
I tried to keep only elements which are really important in example - so I removed fonts, colors, images.
from pytube import YouTube
import tkinter as tk # PEP8: `import *` is not preferred
from threading import Thread
from queue import Queue
# --- functions ---
def update_status():
# check if there are new messages in queue and update correct label
while not queue.empty():
number, text = queue.get()
print('[update_status]:', number, text)
all_labels[number]['text'] = text
root.after(100, update_status) # run again after 100ms
def on_progress(number, queue, stream, chunk, bytes_remaining):
print('[on_progress] bytes_remaining:', bytes_remaining)
queue.put( (number, bytes_remaining) )
def on_complete(number, queue, stream, file_path):
print('[on_complete] file_path:', file_path)
queue.put( (number, file_path) )
def download(url, number, queue):
print('[download] started:', url)
queue.put( (number, 'started') )
yt = YouTube(url,
on_progress_callback=lambda *args:on_progress(number, queue, *args),
on_complete_callback=lambda *args:on_complete(number, queue, *args),
)
video = yt.streams.get_highest_resolution()
video.download()
all_threads[number] = None # inform that finished
print('[download] finished:', url)
#queue.put( (number, 'finished') )
def start_threads():
for number, (t, entry) in enumerate(zip(all_threads, all_entries)):
url = entry.get().strip()
if url:
print('[start_threads]:', number, 'starting')
t = Thread(target=download, args=(url, number, queue))
t.start()
else:
print('[start_threads]:', number, 'empty')
t = None
all_threads[number] = t
# --- main ---
all_threads = [None] * 5 # to have all values at start
all_entries = []
all_labels = []
root = tk.Tk()
queue = Queue()
for n in range(5):
entry = tk.Entry(root, width=50)
entry.grid(column=0, row=n)
all_entries.append(entry)
label = tk.Label(root, width=15, text='-')
label.grid(column=1, row=n)
all_labels.append(label)
button = tk.Button(root, text='Start', command=start_threads)
button.grid(column=0, row=n 1, columnspan=2)
# for test I put some links so I don't have to do it manually
all_entries[0].insert('end', 'https://www.youtube.com/watch?v=aqz-KE-bpKQ')
all_entries[1].insert('end', 'https://www.youtube.com/watch?v=bNfYUsDSrOs')
all_entries[2].insert('end', 'https://www.youtube.com/watch?v=W7LWny6c4wI')
all_entries[3].insert('end', 'https://www.youtube.com/watch?v=A8LRxIANzQs')
update_status()
root.mainloop()