Home > Blockchain >  Python youtube downloader with progress bar
Python youtube downloader with progress bar

Time:12-31

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.

More info: https://github.com/ytdl-org/youtube-dl/blob/3e4cedf9e8cd3157df2457df7274d0c842421945/youtube_dl/YoutubeDL.py#L230

  • Related