Je cherchais un moyen de télécharger les vidéos d'une liste de lecture (playlist) Youtube.
N'ayant rien trouvé d'intéressant, j'ai pondu ce super script : yepisode.py ©

Requiert Python 3.6+ et le module objectpath, ainsi que youtube-dl et PhantomJS.

"""
File: yepisodes.py

Download a complete Youtube playlist.
Python 3.6+, requires the objectpath module, youtube-dl and PhantomJS.

Example:
    python3.6 yepisodes.py \
        "PLBvbiyw33QLlycUu2NL8sk52tBzc1WwV_" \
        "pyconfr2013-conf{number:02d}.mkv"

Set up a proxy if videos are blocked in your country.
Some proxies here: http://www.xroxy.com/proxylist.htm
    export HTTPS_PROXY="socks4://203.91.118.71:1080"
"""

import json
import os
import pathlib
import subprocess
import sys
import time
from typing import Dict, Generator, Tuple

import objectpath

__version__ = '0.0.1'

YT_URL_LIST = 'https://www.youtube.com/playlist?list={}'
YT_URL_VIDEO = 'https://www.youtube.com/watch?v={}'


def download(uid: str, output: pathlib.Path) -> None:
    """ Download a video using youtube-dl. """

    cmd = [
        'youtube-dl',
        '--continue',
        '--format', 'bestvideo+bestaudio',
        '--merge-output-format', output.suffix[1:],
        '--output', output.stem,
        YT_URL_VIDEO.format(uid),
    ]
    start = time.time()

    proc = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
    proc.communicate()
    if proc.returncode != 0:
        print('✗', 'cmd="' + ' '.join(cmd) + '"')
        return

    # The merge may take some time to be reflected on the FS
    for _ in range(10):
        if output.is_file():
            break
        time.sleep(2)
    mio = output.stat().st_size / 1024 / 1024 / (time.time() - start)
    print('✓', '@', '{:.2f}'.format(mio), 'Mio/s')


def download_all(playlist: str, fmt: str) -> None:
    """ Download all videos from a given playlist. """

    for number, idx in find_video_ids(playlist):
        output = pathlib.Path(fmt.format(number=number))
        if output.exists():
            print(output.name, '✓')
            continue

        print(output.name, end=' ')
        sys.stdout.flush()
        download(idx, output)


def find_video_ids(playlist: str) -> Generator[Tuple[int, str], None, None]:
    """ Find video URLs. """

    data = get_json(playlist)
    tree = objectpath.Tree(data)
    videos = tree.execute('$..playlistVideoRenderer.videoId')
    for number, video in enumerate(videos, 1):
        yield number, video


def get_json(uid: str) -> Dict[str, str]:
    """ Find the JSON data from sources of a given playlist URL. """

    # Create the JS configuration file
    filename = '.yepisode-conf.js'
    user_agent = ('Mozilla/5.0 (Windows NT 6.1; WOW64; rv:50.0) '
                  'Gecko/20180326 Firefox/59.0.1')
    content = (f"'use strict';\n"
               f"var page = require('webpage').create();\n"
               f"page.settings.userAgent = '{user_agent}';\n"
               f"page.open('{YT_URL_LIST.format(uid)}', function(status) {{\n"
               f"    console.log(page.content);\n"
               f"    phantom.exit();\n"
               f"}});\n")
    with open(filename, 'w') as fp:
        fp.write(content)

    # Call PhantomJS to interpret the web page JS
    cmd = ['phantomjs', filename]
    try:
        data = subprocess.check_output(cmd).decode('utf-8')
        pattern = 'window["ytInitialData"] = '
        for line in data.splitlines():
            line = line.strip()
            if line.startswith(pattern):
                return json.loads(line[len(pattern):-1])
    finally:
        os.remove(filename)


def main(*args: str) -> int:
    """ Main logic. """

    try:
        download_all(*args[:2])
    except ValueError:
        print('Required arguments: the playlist ID and the output format')
        print('Example:', os.path.basename(sys.executable), __file__,
              '"PLBvbiyw33QLlycUu2NL8sk52tBzc1WwV_"', '"video-{number:02}.mkv"')
        return 1
    return 0


if __name__ == '__main__':
    exit(main(*sys.argv[1:]))

Un exemple afin d'illuster une utilisation :

$ python3.6 yepisodes.py "PLBvbiyw33QLlycUu2NL8sk52tBzc1WwV_" "pyconfr2013-conf_{number:02}.mkv"
pyconfr2013-conf_01.mkv ✓ @ 3.25 Mio/s
pyconfr2013-conf_02.mkv ...

Toutes les vidéos de la PyConFR 2013 seront téléchargées dans le dossier courant. Simple comme bonjour !