有没有办法在异步中使用youtube-dl

问题描述 投票:0回答:2

我有一个应用程序,我使用

zmq
asyncio
与能够使用
youtube-dl
下载视频到服务器的客户端进行通信。我尝试将
await
添加到
youtube_dl
的下载功能,但它给了我一个错误,因为它不是协程。我现在的代码看起来就像这样:

import asyncio
import youtube_dl


async def networking_stuff():
    download = True
    while True:
        if download:
            print("Received a request for download")
            await youtube_to_mp3("https://www.youtube.com/watch?v=u9WgtlgGAgs")
            download = False
        print("Working..")
        await asyncio.sleep(2)


async def youtube_to_mp3(url):
    ydl_opts = {
        'format': 'bestaudio/best',
        'postprocessors': [{
            'key': 'FFmpegExtractAudio',
            'preferredcodec': 'mp3',
            'preferredquality': '192',
        }]
    }

    with youtube_dl.YoutubeDL(ydl_opts) as ydl:
        ydl.download([url])


loop = asyncio.get_event_loop()
loop.create_task(networking_stuff())
loop.run_forever()

给出以下输出:

Received a request for download
[youtube] u9WgtlgGAgs: Downloading webpage
[youtube] u9WgtlgGAgs: Downloading video info webpage
[youtube] u9WgtlgGAgs: Extracting video information
[youtube] u9WgtlgGAgs: Downloading MPD manifest
[download] Destination: The Cardigans - My Favourite Game “Stone Version”-u9WgtlgGAgs.webm
[download] 100% of 4.20MiB in 00:03
[ffmpeg] Destination: The Cardigans - My Favourite Game “Stone Version”-u9WgtlgGAgs.mp3
Deleting original file The Cardigans - My Favourite Game “Stone Version”-u9WgtlgGAgs.webm (pass -k to keep)
Working..
Working..
....
Working..
Working..

而我希望

Working..
消息也打印在
youtube-dl
的消息之间。我在这里遗漏了一些东西还是这对于
async
/
await
来说是不可能的?
ffmpeg
是否阻塞?如果是这样,我可以在
async
中运行下载而不转换为
mp3
还是使用线程是唯一的方法?

python-3.x ffmpeg async-await youtube-dl
2个回答
3
投票

你是对的,你不能简单地使任何函数异步。

您的问题假设 youtube-dl 需要 ffmpeg 才能工作。这并不完全正确,它可以通过自己的方式下载各个流,AFAIK ffmpeg 仅用于将这些流(视频+音频+可能是字幕)复用到一个文件。

如果您使用 ffmpeg,从性能角度来看并没有太多优势,因为如果通过子进程使用它(最有可能的情况),那么至少会生成 1 个成熟的 process 来完成工作。与子进程的交互也可以以非阻塞的方式完成——参见 https://docs.python.org/3/library/asyncio-subprocess.html,但无论如何,如果你的代码为每个任务生成一个进程,它会在这两种情况下都无法很好地扩展。

否则,可能(并且有意义)分叉 youtube-dl 并进行更改,以便所有网络操作都基于 asyncio。这可能需要大量重构,但应该是可行的。

关于您的代码:
首先,函数

youtube_to_mp3
根本不是异步的,因为没有可以执行
await …
表达式的代码路径。如果您从函数定义中删除 async 一词并从
await
中删除
await youtube_to_mp3("…
,则代码
的含义根本不会改变

其次,即使它是异步的,您也没有以允许“并行”执行的方式使用它。

await
关键字的真正含义是:仅在等待的协程完成后,此任务中的控制流才会继续。如果您需要“并行”运行多个协程,则不需要直接一一等待它们。有多种方法可以并行运行协程,例如,您可以使用 https://docs.python.org/3/library/asyncio-task.html#asyncio.gather 并等待生成的“组合”协程,如果所有任务都是同时知道的(但它看起来不像你的情况),或者使用“即发即忘”方法(loop.create_task)。


0
投票

这是Python中的一种现代方法:

import asyncio
from yt_dlp import YoutubeDL

urls = [
    'https://someplayer.int/the-cleaner-series-3-1-the-reunion',
    'https://someplayer.int/solar-system-series-1-5-strange-worlds',
]

async def download_video(ydl, url):
    await asyncio.to_thread(ydl.download, [url])

async def download_all_videos(urls):
    with YoutubeDL() as ydl:
        tasks = [download_video(ydl, url) for url in urls]
        await asyncio.gather(*tasks)

async def main():
    await download_all_videos(urls)

asyncio.run(main())

因此,我们只需为每个 URL 调用

download_video
,而不是传递 URL 列表,并且我们异步运行每个调用。我们创建一个可调用列表并将其传递给
gather
以异步执行它们。

© www.soinside.com 2019 - 2024. All rights reserved.