youtube-dl download 的设计

最近在给播放器写下载 Youtube 源的功能,调用 youtube_dl 的时候, 看了一部分代码,非常简单又优雅,比如一个例子:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
from __future__ import unicode_literals
import youtube_dl


class MyLogger(object):
def debug(self, msg):
pass

def warning(self, msg):
pass

def error(self, msg):
print(msg)


def my_hook(d):
if d['status'] == 'finished':
print('Done downloading, now converting ...')


ydl_opts = {
'format': 'bestaudio/best',
'postprocessors': [{
'key': 'FFmpegExtractAudio',
'preferredcodec': 'mp3',
'preferredquality': '192',
}],
'logger': MyLogger(),
'progress_hooks': [my_hook],
}

with youtube_dl.YoutubeDL(ydl_opts) as ydl:
ydl.download(['https://www.youtube.com/watch?v=BaW_jenozKc'])

hook 和 logger 都是支持自定义的。

各种类型的 Downloader 会继承 FileDownloader, 这里有一个处理 process_hook 的逻辑, 有一个默认的 report_reprocess 的 hook,以及自己定义的 hooks。在一定的时机去触发 _hook_progress, 将事先定义好的 status 传给各个 hook。这里是简化出来的一个结构。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
class FileDownloader(object):
def __init__(self, ydl, params):
"""Create a FileDownloader object with the given options."""
self.ydl = ydl
self._progress_hooks = []
self.params = params
self.add_progress_hook(self.report_progress)

def _hook_progress(self, status):
for ph in self._progress_hooks:
ph(status)

def add_progress_hook(self, ph):
# See YoutubeDl.py (search for progress_hooks) for a description of
# this interface
self._progress_hooks.append(ph)

def report_progress(self, s):
if s['status'] == 'finished':
# logic
return

if self.params.get('noprogress'):
return

if s['status'] != 'downloading':
# logic
return

在 logger 的处理上, 也是直接看是否有自定义的 logger,如果有就使用。

1
2
3
4
if self.params.get('logger') is not None:
self.params['logger'].warning(message)
self.params['logger'].info(message)
self.params['logger'].debug(message)

Downloader 上下文的处理逻辑,也是部分写日志的逻辑.

1
2
3
4
5
6
class ContextDemo(object):
def __enter__(self):
pass

def __exit__(self, *args):
pass

Reference:

关于头图

拍摄自北京,击剑

urlparse 源码阅读
beanstalk 生命周期