pythonpytube

register_on_progress_callback() method of pytube module causing problem


I am trying to make a downloader with a progress bar for youtube videos using pytube, but I am stuck on an error.

My code:

from pytube import YouTube 

def on_progress(stream, chunk, bytes_remaining):
    total_size = stream.filesize
    bytes_downloaded = total_size - bytes_remaining 
    percentage_of_completion = bytes_downloaded / total_size * 100
    print(percentage_of_completion)

url = "https://www.youtube.com/wat
ch?v=XQZgdHfAAjI&list=PLec973iciX1S0bLNOdmIejMVnUnBWpIwz"

yt_obj = YouTube(url).register_on_progress_callback(on_progress)

stream = yt_obj.streams.filter(progressive=True).get_highest_resolution().download()
size = stream.filesize
print(f"file size is {size}")

When I run this code, I get the following error:

Traceback (most recent call last):
  File "pytube_progress.py", line 15, in <module>
    stream = yt_obj.streams.filter(progressive=True).get_highest_resolution().download()
AttributeError: 'NoneType' object has no attribute 'streams'

Interestingly, when I replace this line of code: yt_obj = YouTube(url).register_on_progress_callback(on_progress) with yt_obj = YouTube(url) every thing works fine, and there are no errors.

Documentation for the register_on_progress_callback() function can be found here.


Solution

  • The first problem in your code is, when the line

    yt_obj = YouTube(url).register_on_progress_callback(on_progress)
    

    is executed, since the register_on_progress_callback() doesn't return anything, the variable yt_obj is assigned the value None. Then, when you have yt_obj.streams later in your code, that triggers the AttributeError.

    The second problem is with this line:

    stream = yt_obj.streams.filter(progressive=True).get_highest_resolution().download()
    

    The download() function returns a str, not a Stream.

    Here is a working version of your code, with these two problems fixed:

    from pytube import YouTube
    
    def on_progress(stream, chunk, bytes_remaining):
        total_size = stream.filesize
        bytes_downloaded = total_size - bytes_remaining
        percentage_of_completion = bytes_downloaded / total_size * 100
        print(percentage_of_completion)
    
    url = "https://www.youtube.com/watch?v=XQZgdHfAAjI&list=PLec973iciX1S0bLNOdmIejMVnUnBWpIwz"
    
    # Create the YouTube object first
    yt_obj = YouTube(url)
    
    # Then register the callback
    yt_obj.register_on_progress_callback(on_progress)
    
    # Download the video, getting back the file path the video was downloaded to
    file_path = yt_obj.streams.filter(progressive=True).get_highest_resolution().download()
    print(f"file_path is {file_path}")