youtube-dl 同时下载多个

youtbe-dl multiple downloads at the same time

我有一个 python 应用程序,其中有一个包含多个网址的变量。

此时我使用这样的东西:

for v in arr:
        cmd = 'youtube-dl -u ' + email + ' -p ' + password + ' -o "' + v['path'] + '" ' + v['url']

        os.system(cmd)

但这样我就一个接一个地下载视频。如何同时下载 3 个视频? (不是来自 youtube,所以没有播放列表或频道)

我不需要在 python 中使用多线程,而是多次调用 youtube-dl,拆分数组。所以从python的角度来看可以上线程

使用 Pool:

import multiprocessing.dummy
import subprocess

arr = [
    {'vpath': 'example/%(title)s.%(ext)s', 'url': 'https://www.youtube.com/watch?v=BaW_jenozKc'},
    {'vpath': 'example/%(title)s.%(ext)s', 'url': 'http://vimeo.com/56015672'},
    {'vpath': '%(playlist_title)s/%(title)s-%(id)s.%(ext)s',
     'url': 'https://www.youtube.com/playlist?list=PLLe-WjSmNEm-UnVV8e4qI9xQyI0906hNp'},
]

email = 'my-email@example.com'
password = '123456'

def download(v):
    subprocess.check_call([
        'echo', 'youtube-dl',
        '-u', email, '-p', password,
        '-o', v['vpath'], '--', v['url']])


p = multiprocessing.dummy.Pool(concurrent)
p.map(download, arr)

multiprocessing.dummy.Pool是一个Pool的基于线程的轻量级版本,它更适合这里,因为工作任务只是启动子进程。

请注意,您之前的代码中的 os.system 不是 subprocess.check_call, which prevents the command injection vulnerability

还要注意 youtube-dl output templates 真的很强大。在大多数情况下,您实际上不需要自己定义和管理文件名。

我使用 threading 库实现了同样的事情,这被认为是生成新进程的一种更轻松的方法。

假设:

  • 每个任务都会将视频下载到不同的目录。
import os
import threading
import youtube_dl

COOKIE_JAR = "path_to_my_cookie_jar"

def download_task(videos, output_dir):

    if not os.path.isdir(output_dir):
        os.makedirs(output_dir)

    if not os.path.isfile(COOKIE_JAR):
        raise FileNotFoundError("Cookie Jar not found\n")

    ydl_opts = { 
        'cookiefile': COOKIE_JAR, 
        'outtmpl': f'{output_dir}/%(title)s.%(ext)s'
    }

    with youtube_dl.YoutubeDL(ydl_opts) as ydl:
        ydl.download(videos)


if __name__ == "__main__":

    output_dir = "./root_dir"

    threads = []
    for playlist in many_playlists:
        output_dir = f"{output_dir}/playlist.name"
        thread = threading.Thread(target=download_task, args=(playlist, output_dir))
        threads.append(thread)
    
    # Actually start downloading
    for thread in threads:
        thread.start()
     
    # Wait for all the downloads to complete
    for thread in threads: 
        thread.join()