0

I have written a small python program to download videos from youtube. The function takes a URL as input and then downloads it.

Code:

def download(i):
    global path
    global c
    global threads
    try:
        fh1 = open(path + "done.txt","a") 
        fh = open(path +"err.txt", "a")
        video = pafy.new(i)
        name = str(c) + '. ' + video.title
        c += 1
        try:
            for p in ('|', '?', '\\', '/', ':', '*', '<', '>', '\"'):
                 name = name.replace(p, '_')

            best = video.getbest(preftype="mp4")
            print("Downloading "+name+"...")
            best.download(quiet=False, filepath= path + name + '.' + best.extension)
            print("Done Downloading "+name)
            fh1.write("Done : " +name+ '\n')
            fh1.close()

best.download(quiet=False, filepath= path + name + '.' + best.extension) line is showing the download percentage speed time left and some other stuff but I cannot find a proper way to get the data that is being shown there. I want to store the data in some variable so that I can show the data in some other place or write it in some file.

Sayok Majumder
  • 1,012
  • 13
  • 28

1 Answers1

1

The pafy documentation specifies that the download() function can take a callback argument that gives you periodic updates about the state of the download. This sounds like exactly what you are searching for.

If a callback function is provided, it will be called repeatedly for each chunk downloaded. It must be a function that takes the following five arguments:

  • total bytes in stream, int
  • total bytes downloaded, int
  • ratio downloaded (0-1), float
  • download rate (kbps), float
  • ETA in seconds, float

The documentation even contains a callback example:

import pafy

# callback function, this callback simply prints the bytes received,
# ratio downloaded and eta.
def mycb(total, recvd, ratio, rate, eta):
    print(recvd, ratio, eta)

p = pafy.new("cyMHZVT91Dw")
ba = p.getbestaudio()
filename = ba.download(quiet=True, callback=mycb)

If you want to pass extra information to your callback, you can can use a callable class object:

import pafy

# Callback class
class MyCallback:
    def __init__(self, callbackId):
        self.callbackId = callbackId

    def __call__(self, total, recvd, ratio, rate, eta):
        print("Downloader #{:d}: {:>7.3f} MB {:>6.1f} % {:>10.1f} kBps    ETA: {:>5.1f} s".format(self.callbackId, recvd/(1024*1024), ratio*100, rate, eta))

p = pafy.new("cyMHZVT91Dw")
ba = p.getbestaudio()
filename = ba.download(quiet=True, callback=MyCallback(42))
Finomnis
  • 18,094
  • 1
  • 20
  • 27
  • Thank you for your response I was not being able to understand the documentation properly. – Sayok Majumder Jul 21 '19 at 18:35
  • Can you tell me is it possible to send some extra data to the callback function except the parameters that you have written – Sayok Majumder Jul 22 '19 at 03:29
  • Let me be more clear. I will be updating multiple progress bars using tkinter and also the download function will be threaded so is there any work around – Sayok Majumder Jul 22 '19 at 03:32
  • 4 possible solutions: 1. write multiple callback functions, 2. bind extra parameters in a lambda, 3. bind extra parameters with `type.MethodType`, 4. create multiple objects as callbacks, make them callable with `__call__`. I personally would choose #4, I think. – Finomnis Jul 22 '19 at 06:51
  • Added example to my answer – Finomnis Jul 22 '19 at 07:17