问题

问题

YouTube 的新功能

Youtube 最近启用了以下选项:添加章节在 YouTube 视频中。

在此处输入图片描述

只需在视频描述中添加章节时间戳(开始时间)即可完成。

在此处输入图片描述

相关话题

使用 youtube-dl/yt-dlp 我们可以

下载 YouTube 视频的一部分通过提供start timeduration

提取 YouTube 视频的描述作为.description文件。

问题

是否有一个命令可以将 YouTube 视频的各章节下载为单独的视频文件没有提供start time并且duration它会自动从中提取时间戳.description


答案1

yt-dlp

无需临时脚本。yt-dlpyoutube-dl 的分支,具有附加功能和修复, 喜欢--split-chapters

用法:

yt-dlp --split-chapters  [OPTIONS] URL [URL...]

安装在 Linux 或 Mac 上:brew install yt-dlp


有关的如何使用 youtube-dl 列出 YouTube 视频的章节标题

答案2

您不能直接执行此操作。我编写了一个 Python 脚本,它将下载整个文件,处理描述文件,然后将下载的文件拆分为其组成章节。

用法 - 如果你将其保存到名为“yt_chapters.py”的文件中,然后运行

yt_chapters.py --split "https://www.youtube.com/watch?v=m5lp8S-YgrQ"

您可以传递多个下载链接,它将循环遍历列表。

章节记录将会添加到原始下载的MP4中。

要求:youtubedl、ffmpeg 和 MP4Box (GPAC)。如果您不想保留原始 MP4,或者您不介意它有章节,您可以注释掉该部分。youtubedl 和 ffmpeg 应该在您的系统路径中,否则请更改以下代码以包含可执行文件的完整路径。

#! /usr/bin/env python
# -*- coding: utf-8 -*-

import subprocess, os, argparse, datetime, re, sys

def get_source_name(download_source:str)->str:

    # Get the expected filename in order to process everything down the line
    result = subprocess.run(["youtube-dl", "--get-filename",
                            "-o", "%(title)s", download_source],
                            capture_output=True, encoding='UTF8')
    return result.stdout.rstrip('\n')

def get_best_mp4_single_file(download_source:str, description_file:str,
                             fix_required:bool=False)->list:

    # Download the file as mp4 (in order to use MP4)
    subprocess.run(["youtube-dl", "--write-description", "-f",
                    "bestvideo[ext=mp4]+bestaudio[ext=m4a]/mp4",
                    "-o", "%(title)s.%(ext)s", link_to_download])
    if fix_required:
        input("Fix the description file to have the chapters "
              "on their own lines in the format timecode name "
              "and then press Enter to continue...")
    return read_description_file(description_file=description_file)

def get_worst_mp4_single_file(download_source:str, description_file:str,
                              fix_required:bool=False)->list:

    # Download the file as mp4 (in order to use MP4)
    subprocess.run(["youtube-dl", "--write-description", "-f",
                    "worstvideo[ext=mp4]+worstaudio[ext=m4a]/mp4",
                    "-o", "%(title)s.%(ext)s", link_to_download])
    if fix_required:
        input("Fix the description file to have the chapters "
              "on their own lines in the format timecode name "
              "and then press Enter to continue...")
    return read_description_file(description_file=description_file)

def add_chapters_to_mp4(chapter_file_name:str, name_for_download:str)->None:

    # Use MP4Box to mux the chapter file with the mp4
    subprocess.run(["MP4Box", "-chap", chapter_file_name, name_for_download])

def read_description_file(description_file:str)->list:

    # Read the description file
    # Split into time and chapter name

    list_of_chapters = []

    with open(description_file, 'r') as f:
        # only increment chapter number on a chapter line
        # chapter lines start with timecode
        line_counter = 1
        for line in f:
            result = re.search(r"\(?(\d?[:]?\d+[:]\d+)\)?", line)
            try:
                # result = re.search("\(?(\d+[:]\d+[:]\d+)\)?", line)
                time_count = datetime.datetime.strptime(result.group(1), '%H:%M:%S')
            except:
                try:
                    # result = re.search("\(?(\d+[:]\d+)\)?", line)
                    time_count = datetime.datetime.strptime(result.group(1), '%M:%S')
                except:
                    continue
            chap_name = line.replace(result.group(0),"").rstrip(' :\n')
            chap_pos = datetime.datetime.strftime(time_count, '%H:%M:%S')
            list_of_chapters.append((str(line_counter).zfill(2), chap_pos, chap_name))
            line_counter += 1

    return list_of_chapters

def write_chapters_file(chapter_file:str, chapter_list:tuple)->None:

            #open(chapter_file, 'w').close()

            # Write out the chapter file based on simple MP4 format (OGM)
            with open(chapter_file, 'w') as fo:
                for current_chapter in chapter_list:
                    fo.write(f'CHAPTER{current_chapter[0]}='
                            f'{current_chapter[1]}\n'
                            f'CHAPTER{current_chapter[0]}NAME='
                            f'{current_chapter[2]}\n')


def split_mp4(chapters:list, download_filename:str, download_name:str)->None:

    # current_duration = subprocess.run(['ffprobe', '-i', download_filename,
                                       # '-show_entries', 'format=duration',
                                       # '-v', 'quiet', '-of', 'csv="p=0"'],
                                        # capture_output=True, encoding='UTF8')
    current_duration_pretext = subprocess.run(['ffprobe', '-i', download_filename,
                                       '-show_entries', 'format=duration',
                                       '-v', 'quiet'],
                                        capture_output=True, encoding='UTF8')
    current_duration = float(current_duration_pretext.stdout[18:-13])
    m, s = divmod(current_duration, 60)
    h, m = divmod(m, 60)
    current_dur = ':'.join([str(int(h)),str(int(m)),str(s)])
    for current_index, current_chapter in enumerate(chapters):
        # current_chapter will be a tuple: position, timecode, name
        next_index = current_index + 1
        start_time = current_chapter[1]
        try:
            end_time = chapters[next_index][1]
        except:
            end_time = current_dur
        output_name = f'{download_name} - ({current_chapter[2]}).mp4'
        subprocess.run(["ffmpeg", "-ss", start_time, "-to", end_time,
                        "-i", download_filename, "-acodec", "copy",
                        "-vcodec", "copy", output_name])

if __name__ == '__main__':

    parser = argparse.ArgumentParser(description='Download Youtube videos with chapters')

    parser.add_argument('links', metavar='N', type=str, nargs='+',
                        help='List of links to download')

    parser.add_argument("--split", help="Split chapters into individual files",
                        action="store_true")
    parser.add_argument("--test", help="Download worst version to speed up testing",
                        action="store_true")
    parser.add_argument("--fix", help="Pause to allow manual correction of the description file",
                        action="store_true")
    args = parser.parse_args()
    # print(args.links)

    for link_to_download in args.links:

        download_name = get_source_name(download_source=link_to_download)
        download_filename = f'{download_name}.mp4'
        description_file = f'{download_name}.description'
        chapter_file = f'{download_name}_chapter.txt'

        if not args.test:
            chapters = get_best_mp4_single_file(download_source=link_to_download,
                                                description_file=description_file,
                                                fix_required=args.fix)
        else:
            chapters = get_worst_mp4_single_file(download_source=link_to_download,
                                                description_file=description_file,
                                                fix_required=args.fix)

        
        if not chapter_file:
            print("No chapters found")
            sys.exit(1)
        
        write_chapters_file(chapter_file=chapter_file, chapter_list=chapters)

        if args.split:
            split_mp4(chapters=chapters, download_filename=download_filename,
                      download_name=download_name)
        
        add_chapters_to_mp4(chapter_file_name=chapter_file,
                              name_for_download=download_filename)

        # Clean up the text files
        os.remove(description_file)
        os.remove(chapter_file)

答案3

在终端上使用此 yt-dlp 命令

安装后,按照文档中的说明yt-dlp使用$ python3 -m pip install -U yt-dlp这里

cd 到您选择的目录并运行以下命令。

yt-dlp  -f 'best[height=720]' [URL...]  -o '%(title)s/%(title)s.%(ext)s'  --split-chapters  -o "chapter:%(title)s/[%(section_number)s] - %(section_title)s.%(ext)s" 

记得更新[URL...]视频网址(删除方括号)。您可能ffmpeg还需要安装。谢谢

相关内容