---
tags: [tooling]
verified: 2026-09-02
---

# yt-dlp: извлечение субтитров и подготовка текста для LLM

> Утилита называется **yt-dlp**, а не `yt-dl`. Оригинальный `youtube-dl` фактически
> заброшен (последний значимый релиз — конец 2021) и регулярно ломается на актуальном
> YouTube из-за изменений в плеере. Активный форк — `yt-dlp`, drop-in замена с тем же
> синтаксисом плюс существенные расширения. Везде ниже имеется в виду он.

## Архитектура: что вообще происходит при извлечении субтитров

yt-dlp состоит из набора **extractor**'ов (по одному на сайт) и слоя
**post-processor**'ов. При запросе субтитров extractor парсит страницу/плеер YouTube,
находит ссылки на дорожки субтитров и скачивает их в том формате, который отдаёт сам
сайт. Для YouTube это **VTT** (WebVTT) или внутренний **srv3/json3**; формат `srt`
YouTube напрямую не отдаёт — его yt-dlp получает конвертацией через ffmpeg
(post-processor `--convert-subs`).

Из этого следуют два практических факта:

1. **`--sub-format srt` не гарантирует srt напрямую.** yt-dlp скачает доступный формат
   (обычно vtt) и сконвертирует. Поэтому для конвертации нужен установленный **ffmpeg**.
2. Различают два типа дорожек: **загруженные автором** (`--write-subs`, ключ `subtitles`)
   и **автоматически сгенерированные** распознаванием речи (`--write-auto-subs`, ключ
   `automatic_captions`). Это разные сущности — у большинства YouTube-роликов есть только
   вторые. Для полноты обычно запрашивают оба типа.

## Базовые операции

Сначала всегда полезно посмотреть, какие дорожки вообще есть, не скачивая ничего:

```bash
yt-dlp --list-subs "https://www.youtube.com/watch?v=jNQXAC9IVRw"
```

Вывод разделён на `Available subtitles` (ручные) и `Available automatic captions`
(автогенерация, обычно десятки языков, многие — машинный перевод).

Скачать субтитры без видео:

```bash
# ручные + авто, английский, в исходном формате (vtt), без видео
yt-dlp --skip-download --write-subs --write-auto-subs --sub-langs en \
  "https://www.youtube.com/watch?v=jNQXAC9IVRw"
```

Ключевые опции:

- `--skip-download` — не качать сам медиафайл (для текста — всегда).
- `--write-subs` / `--write-auto-subs` — ручные / автоматические дорожки.
- `--sub-langs en,ru` — список языков. `all` — всё; `en.*` — все варианты английского
  (например `en-US`, `en-orig`); `en-orig` у YouTube означает оригинальную дорожку до
  перевода.
- `--sub-format vtt/srt/best` — желаемый формат (для YouTube реально доступны vtt/srv*).
- `--convert-subs srt` — пост-конвертация в srt/ass/lrc/vtt через ffmpeg.
- `-o` / `--output` — шаблон имени файла (см. ниже).

Файл получит имя по шаблону `%(title)s [%(id)s].%(ext)s`, ext будет вида `en.vtt`.

## Выбор языка: оригинал, а не перевод

Ключевое различие — между **оригинальной дорожкой** и **машинным переводом**. YouTube
делает автоматическое распознавание речи (ASR) на языке оригинала, а любой другой язык
в списке доступных автодорожек генерирует машинным переводом этой дорожки. То есть для
англоязычного ролика `--sub-langs ru` даст текст, дважды прогнанный через автоматику:
сначала ASR со своими ошибками, затем машинный перевод поверх этих ошибок. Качество
деградирует на каждом шаге.

Отсюда правило: **всегда бери дорожку на языке оригинала, а не перевод под удобный
язык.** Для LLM это вдвойне верно — модель свободно анализирует и английский, и русский
на входе, а качество анализа определяется качеством исходного текста, а не его языком.
Чистый оригинал лучше зашумлённого машинного перевода. Язык результата задаётся промптом
к модели и от языка транскрипта не зависит.

Практически: для русскоязычного ролика оригинал — это `ru`, для англоязычного — `en`;
машинный перевод (`ru` поверх английского ролика) не брать никогда. YouTube помечает
оригинальную автодорожку суффиксом `-orig`, поэтому при неизвестном заранее языке можно
перечислить приоритеты — yt-dlp возьмёт первый доступный из списка:

```bash
yt-dlp --skip-download --write-auto-subs --sub-langs "en-orig,en,ru-orig,ru" \
  --sub-format json3 -o "%(id)s.%(ext)s" "URL"
```

Это работает, но хрупко на разноязычных каналах: порядок приоритетов фиксирован. Для
надёжного выбора оригинала язык определяют программно по метаданным (поле
`automatic_captions`, дорожка с суффиксом `-orig`) — но это уже задача твоего скрипта.

Отдельный приоритет — **субтитры, залитые автором** (`subtitles`, не
`automatic_captions`). Если они есть, бери их раньше любой автогенерации, включая
оригинальный ASR: они вычитаны человеком и качественнее. Запрос обоих типов
(`--write-subs --write-auto-subs`) даёт yt-dlp выбрать ручные, когда они доступны.

## Получение чистого текста без тайм-кодов

Это твой основной сценарий, и здесь есть подводный камень. **Сырой VTT от YouTube
непригоден для прямого `grep`-удаления тайм-кодов.** Автосубтитры YouTube используют
эффект "roll-up": каждая реплика дублируется в нескольких соседних cue, а внутри строк
стоят инлайн-теги тайминга по словам (`<00:00:01.234><c> word</c>`). Наивная фильтрация
тайм-кодов даст текст с массой повторов.

### Вариант 1 — рекомендуемый: srv3/json3 как источник

YouTube отдаёт формат `json3` (он же srv3), где текст структурирован и не содержит
roll-up-дублей. Самый чистый путь — скачать именно его и собрать текст собственной
постобработкой:

```bash
yt-dlp --skip-download --write-auto-subs --sub-langs en --sub-format json3 \
  -o "transcript" "https://www.youtube.com/watch?v=jNQXAC9IVRw"
```

Структура файла: объект с массивом `events`, в каждом событии — массив `segs`, где
полезный текст лежит в поле `utf8`. Чтобы получить связный текст, достаточно пройти по
`events[].segs[].utf8`, отбросить пустые сегменты и склеить с нормализацией пробелов и
переносов. Никакой временнóй разметки в результате не остаётся — это и есть оптимальный
вход для LLM. (Сам разбор делается отдельным скриптом — здесь он не приводится.)

### Вариант 2 — простой: конвертация в srt + чистка

Если json3 по какой-то причине недоступен, работает связка `--convert-subs srt` плюс
постобработка. yt-dlp при конвертации vtt→srt уже схлопывает roll-up-дубли, поэтому из
srt текст вычищается надёжно:

```bash
yt-dlp --skip-download --write-auto-subs --sub-langs en --convert-subs srt \
  -o "transcript" "https://www.youtube.com/watch?v=jNQXAC9IVRw"
```

Получится `transcript.en.srt` вида:

```text
1
00:00:00,000 --> 00:00:02,500
Hello and welcome to the channel
```

Чистка в plain text одной командой (удаляем номера, тайм-коды и пустые строки, склеиваем):

```bash
sed -E '/^[0-9]+$/d; /-->/d; /^[[:space:]]*$/d' transcript.en.srt \
  | tr '\n' ' ' | sed -E 's/[[:space:]]+/ /g' > transcript.txt
```

Разбор фильтров `sed`: `/^[0-9]+$/d` убирает строки-номера cue, `/-->/d` — строки
тайм-кодов, `/^[[:space:]]*$/d` — пустые. Затем `tr` склеивает переносы в пробелы и
финальный `sed` схлопывает кратные пробелы.

### Вариант 3 — встроенный downloader без ffmpeg

Если ffmpeg ставить не хочешь, у автодорожек YouTube часто доступен формат `vtt`, но
для чистого текста его всё равно надо обработать. Тогда проще json3 (вариант 1) —
ffmpeg для него не нужен, обработка чисто текстовая.

**Вывод по форматам для LLM:** бери **json3** (вариант 1) — это самый устойчивый к
дублям путь и без зависимости от ffmpeg. srt-связка (вариант 2) — хорошая запасная,
когда хочется одной строкой и ffmpeg уже есть. Чистого `--sub-format txt`, который бы
сразу давал готовый текст без тайм-кодов, у yt-dlp нет — постобработка неизбежна.

## Пакетная обработка канала

Для твоей задачи (LLM по содержимому каналов) обычно нужен не один ролик, а весь канал
или плейлист. yt-dlp принимает URL канала/плейлиста напрямую и обходит все видео.

```bash
yt-dlp --skip-download --write-auto-subs --sub-langs en --sub-format json3 \
  -o "%(channel)s/%(upload_date)s_%(id)s.%(ext)s" \
  "https://www.youtube.com/@channelname/videos"
```

Полезное при массовом обходе:

- `--download-archive archive.txt` — ведёт журнал уже обработанных id; повторный запуск
  скачивает только новые видео. Это база для инкрементального обновления.
- `--sleep-requests 1` и `--min-sleep-interval`/`--max-sleep-interval` — паузы между
  запросами, чтобы не словить временную блокировку (HTTP 429) при обходе сотен видео.
- `--match-filter "duration > 60"` — отсев, например, Shorts.
- `-I 1:50` (`--playlist-items`) — срез по диапазону.
- `--lazy-playlist` — начинать обход, не дожидаясь полного разбора плейлиста.

Только метаданные без скачивания чего-либо (заголовки, описания, длительности —
пригодится для индексации перед тем как качать субтитры):

```bash
yt-dlp --skip-download --print "%(id)s\t%(upload_date)s\t%(title)s" \
  "https://www.youtube.com/@channelname/videos" > index.tsv
```

## Эксплуатационные нюансы

- **Свежесть версии критична.** YouTube периодически меняет формат плеера, и устаревший
  yt-dlp отваливается с ошибками извлечения. Самообновляющаяся установка: `yt-dlp -U`.
  Установка из pip: `pip install -U yt-dlp`. Релизы выходят примерно раз в месяц
  (последний на сентябрь 2026 — `2026.08.19`), а рядом живёт nightly-ветка с более
  частыми фиксами extractor'ов.
- **Безопасность.** За 2026 год закрыты две инъекции произвольных команд: CVE-2026-26331
  через `--netrc-cmd` (исправлено в `2026.02.21`) и CVE-2026-55404 через недостаточную
  санитизацию вывода `--write-link` (исправлено в `2026.08.19`). Ещё одна причина не
  сидеть на старой версии.
- **Блокировки и "Sign in to confirm you're not a bot".** При активном обходе YouTube
  может требовать аутентификацию. Решается передачей cookies:
  `--cookies-from-browser firefox` или `--cookies cookies.txt`. Не злоупотребляй частотой
  запросов с залогиненного аккаунта.
- **ffmpeg как зависимость.** Нужен для `--convert-subs` и любой пост-конвертации. Для
  пути через json3 не требуется.
- **Кодировки.** json3 и vtt от YouTube — UTF-8. При последующей обработке в
  Windows-окружении явно работай в UTF-8, иначе кириллица и спецсимволы поедут.

## Итог

Для подготовки текста под LLM оптимальный путь — скачивать субтитры в формате **json3**
и собирать текст из структуры `events[].segs[].utf8` собственной постобработкой, получая
связный текст без тайм-кодов и без roll-up-дублей, без зависимости от ffmpeg. srt-связка
с чисткой через `sed` — надёжная запасная при наличии ffmpeg. По языку: всегда бери
оригинальную дорожку (для русскоязычных каналов `ru`, для англоязычных `en`), приоритет
ручным субтитрам автора над автогенерацией, и никогда — машинный перевод; язык ответа
задаётся промптом к модели. Для каналов обязательно `--download-archive` для
инкрементальности и троттлинг запросов против блокировок.

---

## Связанные заметки

- [Медиасервер: Transmission + Sonarr/Radarr + Plex](./media-server-guide.md) — смежный сценарий работы с медиа
- [Локальный инференс LLM: движки, квантование, бюджет памяти](./llm-local-inference.md) — что происходит с этим текстом дальше: контекстное окно и его цена
