I'm trying to build GUI youtube song downloader. I succeeded to write a program that can get several links and download them to a specific location. But the program gets stuck while downloading, so I want to add a progress bar so the user can see the download progress.
The program I wrote is:
def Download_Songs():
links=LinkText.get("1.0",'end-1c').split()
directory=SaveEntry.get()
# importing packages
import youtube_dl
import shutil
import os
ydl_opts = {'postprocessors': [{'key': 'FFmpegExtractAudio',
'preferredcodec': 'mp3',
'preferredquality': '192'}]}
for link in links:
with youtube_dl.YoutubeDL(ydl_opts) as ydl:
ydl.download([link])
for file in os.listdir("."):
if file.endswith(".mp3"):
newfile = file[:-16] ".mp3"
shutil.move(file, os.path.join(directory, newfile)) # move the file without the search title
def SearchDir():
location = filedialog.askdirectory()
directory.set(location)
from tkinter import filedialog
import tkinter as tk
root = tk.Tk()
# Label for the links to download
LinksFrame = tk.Frame(root)
LinkLabel = tk.Label(LinksFrame, text="Enter links here")
LinkText = tk.Text(LinksFrame, width=65, height=15)
LinksFrame.pack(pady=6)
LinkLabel.pack(side=tk.LEFT)
LinkText.pack(side=tk.RIGHT)
# This frame is for the downloaded directory.
directory = tk.StringVar()
directory.set("")
LocationFrame = tk.Frame(root)
SaveLabel = tk.Label(LocationFrame, text=" Download to:", padx=3)
SaveEntry = tk.Entry(LocationFrame, text=directory, width=80)
SaveButton = tk.Button(LocationFrame, text="Browse", command=SearchDir)
LocationFrame.pack(pady=3)
SaveLabel.pack(side=tk.LEFT)
SaveEntry.pack(side=tk.LEFT)
SaveButton.pack(side=tk.RIGHT, padx=5)
# download button
DownloadButton = tk.Button(root, text="Download", command=Download_Songs)
DownloadButton.pack(pady=7)
root.mainloop()
I searched google for a solution and several question here got the answer to add this lines to my code:
ydl_opts = {
'format': 'bestaudio/best',
'outtmpl': '%(id)s',
'noplaylist' : True,
'progress_hooks': [my_hook],
}
def my_hook(d):
if d['status'] == 'finished':
print('Done downloading, now converting ...')
This raises me an error and this is expected as my_hook is not declared. I don't understand where I need to declare it....
Exception in Tkinter callback
Traceback (most recent call last):
File "C:\Users\Admin\AppData\Local\Programs\Python\Python310\lib\tkinter\__init__.py",
line 1921, in __call__
return self.func(*args)
File "Z:\BackUp\programs by me\Python progs\YtDown.py", line 13, in Download_Songs
'progress_hooks': [my_hook]}]}
UnboundLocalError: local variable 'my_hook' referenced before assignment
So the question is how I add those lines...
CodePudding user response:
With this code:
ydl_opts = {
'format': 'bestaudio/best',
'outtmpl': '%(id)s',
'noplaylist' : True,
'progress_hooks': [my_hook],
}
def my_hook(d):
if d['status'] == 'finished':
print('Done downloading, now converting ...')
you are referencing my_hook
before it's declared. Move the function before it.
Also, you need to merge it with your existing options.
Try this:
def my_hook(d):
if d['status'] == 'finished':
print('Done downloading, now converting ...')
def Download_Songs():
[...]
ydl_opts = {
'format': 'bestaudio/best',
'outtmpl': '%(id)s',
'noplaylist' : True,
'progress_hooks': [my_hook],
'postprocessors': [{'key': 'FFmpegExtractAudio',
'preferredcodec': 'mp3',
'preferredquality': '192'}]
}
[...]
This won't actually make a progress bar, though. What it does is adds my_hook
to the progress hooks. my_hook
, in your code, checks if the download is finished. If so, it outputs a message to the command-line.
What you need to do is check if the status is "downloading" and if so, there will be information like "downloaded_bytes" and "total_bytes" which will be helpful for a progress bar. Then if the status is "finished", the download is finished, but youtube-dl isn't necessarily done (it might still need to convert the video to another format). At that point, though, I'm not sure there's a good way to continue the progress bar other than keeping it at 99%.
After that, you just need to make a progress bar with the information the hook gives you.
I can't test this right now, but it should work. Let me know if you have any questions or it doesn't work.