Вопрос или проблема
Новая функция YouTube
YouTube недавно включил возможность добавлять главы в видео на YouTube.
Это делается просто добавлением временных меток глав (время начала) в описании видео.
Связанные темы
Используя youtube-dl/yt-dlp, мы можем:
- скачать часть видео с YouTube, указав
время начала
ипродолжительность
. - извлечь описание видео с YouTube в файлы
.description
.
Вопрос
Существует ли одна команда, чтобы скачать главы видео на YouTube в виде отдельных файлов видео без указания времени начала
и продолжительности
, а просто автоматически извлечь временные метки из .description
?
yt-dlp
Нет необходимости в произвольных скриптах. yt-dlp является форком youtube-dl с дополнительными функциями и исправлениями, такими как --split-chapters
.
Использование:
yt-dlp --split-chapters [OPTIONS] URL [URL...]
Установка на Linux или Mac, с помощью pip
python3 -m pip install -U "yt-dlp[default]"
или brew install yt-dlp
.
Связано: Как получить список названий глав из видео на YouTube с помощью youtube-dl.
Вы не можете сделать это напрямую. Я написал скрипт на 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:
# Получить ожидаемое имя файла для дальнейшей обработки
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:
# Скачать файл в формате mp4 (для использования 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("Исправьте файл описания, чтобы главы "
"были на отдельных строках в формате временная метка имя "
"и затем нажмите Enter, чтобы продолжить...")
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:
# Скачать файл в формате mp4 (для использования 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("Исправьте файл описания, чтобы главы "
"были на отдельных строках в формате временная метка имя "
"и затем нажмите Enter, чтобы продолжить...")
return read_description_file(description_file=description_file)
def add_chapters_to_mp4(chapter_file_name:str, name_for_download:str)->None:
# Использовать MP4Box для объединения файла глав с mp4
subprocess.run(["MP4Box", "-chap", chapter_file_name, name_for_download])
def read_description_file(description_file:str)->list:
# Читать файл описания
# Разделить на время и название главы
list_of_chapters = []
with open(description_file, 'r') as f:
# увеличивать номер главы только на строке главы
# строки глав начинаются с временной метки
line_counter = 1
for line in f:
result = re.search(r"\(?(\d?[:]?\d+[:]\d+)\)?", line)
try:
time_count = datetime.datetime.strptime(result.group(1), '%H:%M:%S')
except:
try:
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:
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_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):
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='Скачать видео YouTube с главами')
parser.add_argument('links', metavar="N", type=str, nargs="+",
help='Список ссылок для скачивания')
parser.add_argument("--split", help="Разделить главы на отдельные файлы",
action="store_true")
parser.add_argument("--test", help="Скачать худшую версию для ускорения тестирования",
action="store_true")
parser.add_argument("--fix", help="Пауза для ручного исправления файла описания",
action="store_true")
args = parser.parse_args()
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("Главы не найдены")
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)
# Удалить текстовые файлы
os.remove(description_file)
os.remove(chapter_file)
Используйте эту команду 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...]
на URL видео (уберите квадратные скобки). Вам также может понадобиться установить FFmpeg.
Ответ или решение
Чтобы загрузить главы видео с YouTube в виде отдельных видеофайлов, вы можете использовать программу yt-dlp, являющуюся форком youtube-dl с дополнительными функциональными возможностями, включая опцию --split-chapters
. В этом руководстве я подробно опишу, как это сделать, обеспечив удобочитаемость и полноту ответа.
Установка необходимые инструментов
Перед началом работы убедитесь, что у вас установлены следующие программы:
- Python: требуется для установки yt-dlp через pip.
- yt-dlp: выполните следующую команду в терминале:
python3 -m pip install -U yt-dlp
- FFmpeg: необходим для обработки видеофайлов. Установите его следующими способами:
- На Linux (Debian/Ubuntu):
sudo apt update sudo apt install ffmpeg
- На macOS:
brew install ffmpeg
- На Linux (Debian/Ubuntu):
Загрузка глав видео
Теперь, когда ваши инструменты установлены, перейдем к процессу загрузки глав видео. Используйте следующую команду в терминале:
yt-dlp -f 'best[height=720]' [URL...] --split-chapters -o "chapter:%(title)s/[%(section_number)s] - %(section_title)s.%(ext)s"
Где:
- [URL…]: замените на ссылку на видео YouTube, главы которого вы хотите загрузить.
- Параметр
-f 'best[height=720]'
указывает на загрузку лучшего видео с высотой 720 пикселей. - Опция
--split-chapters
обеспечивает разделение глав на отдельные файлы. - Параметр
-o "chapter:%(title)s/[%(section_number)s] - %(section_title)s.%(ext)s"
задает формат именования файлов для каждой главы.
Пример использования
Если вы хотите загрузить видео с данной ссылкой:
https://www.youtube.com/watch?v=b1Fo_M_tj6w
Выполните команду:
yt-dlp -f 'best[height=720]' https://www.youtube.com/watch?v=b1Fo_M_tj6w --split-chapters -o "chapter:%(title)s/[%(section_number)s] - %(section_title)s.%(ext)s"
Заключение
Теперь вы знаете, как загружать главы видео с YouTube как отдельные видеофайлы с помощью yt-dlp. Этот процесс универсален и может применяться ко всем видео, которые имеют разделы. Надеюсь, это руководство было полезным. Если возникнут дополнительные вопросы, не стесняйтесь спрашивать!