Это черновой разбор пути: какую задачу мы решали, какие решения пробовали, где ломались и к какой архитектуре пришли.
Задача
Мне нужен был собственный микросервис для вытаскивания транскриптов из YouTube-видео. Не очередной внешний сайт, не ручной процесс, не «скачай файл и передай агенту», а свой стабильный endpoint.
У сервиса было две цели:
- веб-интерфейс для человека: вставить ссылку на YouTube и получить текст;
- API для агентов вроде OpenClaw и Hermes, чтобы они могли сами получать транскрипт по ссылке на видео.
Финальная точка входа:
https://app.dima.cloud/transcribe/
API для агентов:
GET https://app.dima.cloud/transcribe/api/transcript?url=YOUTUBE_URL
GET https://app.dima.cloud/transcribe/api/transcript.txt?url=YOUTUBE_URL
Первый прототип
На старте было два файла: Python-скрипт и React UI. Python-скрипт уже умел пробовать два способа получения субтитров:
youtube-transcript-apiyt-dlp
Но UI был непригоден для self-hosted сценария: он не вызывал наш backend, а пытался идти в сторонний API прямо из браузера. Поэтому мы заменили это на более простую и управляемую архитектуру:
FastAPI backend
простая HTML-страница
systemd
nginx route
Что было развернуто
На сервере сервис живет здесь:
/opt/youtube-transcribe-service
Запускается через systemd:
youtube-transcribe.service
Внутри слушает локально:
127.0.0.1:8092
Nginx проксирует публичный путь:
/transcribe/ -> 127.0.0.1:8092
JSON endpoint возвращает структуру такого вида:
{
"success": true,
"transcript": "...",
"language": "en",
"word_count": 3215,
"char_count": 17150,
"video_id": "z02Y-1OvWSM",
"method": "ytdlp"
}
Plain text endpoint возвращает только текст транскрипта, что удобнее для агентов.
Первая проблема: nginx и внутренний порт
Сначала путь /transcribe редиректил на неправильный URL:
https://app.dima.cloud:2443/transcribe/
Причина была в том, что HTTPS server block внутри nginx слушал внутренний порт 127.0.0.1:2443, а перед ним стоял внешний роутинг. Nginx сформировал absolute redirect с внутренним портом.
Решение: убрать редирект и сделать так, чтобы оба пути отдавали страницу:
/transcribe
/transcribe/
Главная проблема: YouTube не любит VPS IP
Самая большая сложность оказалась не в FastAPI, nginx или UI. Главная проблема была в egress IP.
Сервер выходит в интернет с VPS/cloud IP. YouTube часто режет такие адреса. Типовые ошибки:
Sign in to confirm you're not a bot
YouTube is blocking requests from your IP
HTTP Error 429: Too Many Requests
Это проявлялось и в youtube-transcript-api, и в yt-dlp. То есть проблема была не в одной конкретной библиотеке.
Попытка 1: cookies из браузера
Первый бесплатный обход: экспортировать YouTube cookies из браузера и положить их на сервер:
/opt/youtube-transcribe-service/cookies.txt
Сервис умел использовать этот файл через переменную:
YOUTUBE_TRANSCRIPT_COOKIE_PATH=/opt/youtube-transcribe-service/cookies.txt
Метод временно помогал, но быстро проявился его главный недостаток: cookies ротируются и инвалидируются. yt-dlp прямо сообщил:
The provided YouTube account cookies are no longer valid.
They have likely been rotated in the browser as a security measure.
Вывод: cookies — полезный временный workaround, но не production-архитектура. Их нужно регулярно обновлять, особенно после смены VPN, IP или подозрительной активности.
Попытка 2: reverse SOCKS через Mac
Вторая бесплатная попытка: пустить серверный трафик через мой Mac.
сервер -> SSH reverse SOCKS -> Mac -> интернет
Когда Mac выходил в интернет напрямую, YouTube видел домашний IP, а не VPS. Это работало.
Но если Mac был подключен к VPN на этом же сервере, получалась петля:
сервер -> Mac -> VPN -> тот же сервер -> YouTube
Вывод: технически красиво, но хрупко. Mac должен быть включен, туннель должен жить, VPN может ломать маршрут. Для сервиса, которым пользуются агенты, это не подходит.
Попытка 3: Webshare static proxies
Дальше мы проверили тестовый список Webshare из 10 static proxies.
Каждый прокси прогонялся через реальные тесты:
- отвечает ли IP-check;
- видит ли YouTube список субтитров;
- может ли скачать реальный файл субтитров.
Результат: ни один из 10 не подошел. Часть получала 429 Too Many Requests, часть не могла скачать usable formats, часть таймаутилась.
Интересная деталь: некоторые прокси могли показать список субтитров через --list-subs, но падали именно на скачивании timedtext-файлов.
Вывод: static/datacenter proxies для этой задачи почти бесполезны.
Рабочее решение: DataImpulse Residential Proxy
Рабочим решением оказался residential proxy от DataImpulse.
Проверка через сервер:
curl --proxy "http://user:pass@gw.dataimpulse.com:823/" https://api.ipify.org
Внешний IP стал residential, не VPS. После этого yt-dlp смог скачать реальные .vtt субтитры:
- контрольное видео
z02Y-1OvWSM; - проблемное видео
GEzbesM_X3U.
Production-сервис был переключен на:
YOUTUBE_PROXY_URL=http://user:pass@gw.dataimpulse.com:823/
После перезапуска:
systemctl restart youtube-transcribe.service
публичные endpoints начали работать стабильно.
Упрощение интерфейса
Изначально в UI были настройки языка и метода:
language: ru / en / auto
method: api / ytdlp / auto
Но на практике они только мешали. Почти всегда пользователю нужен лучший доступный транскрипт. Поэтому интерфейс был упрощен:
YouTube URL -> Получить -> Транскрипт или ошибка
Backend сохранил обратную совместимость для агентов, но в UI осталась только ссылка.
Итоговая архитектура
Пользователь или агент
↓
https://app.dima.cloud/transcribe/
↓
FastAPI на VPS
↓
yt-dlp / youtube-transcript-api
↓
DataImpulse Residential Proxy
↓
YouTube timedtext/subtitles
↓
текст транскрипта
Что я понял
- Сделать UI и API для транскриптов просто.
- Стабильно получать YouTube subtitles с VPS — сложно.
- Главная проблема не библиотека, а IP-репутация.
- Cookies помогают, но быстро становятся операционным долгом.
- Домашний туннель работает, но не подходит для автономного сервиса.
- Static proxies не решают задачу.
- Residential proxy решает.
Что стоит добавить дальше
- Кэширование транскриптов по
video_id, чтобы не тратить proxy-трафик повторно. - Rate limiting для агентов.
- API key, потому что endpoint публичный.
- Отдельный proxy healthcheck.
- Очередь задач для длинных видео.
- Более точные ошибки: no subtitles, proxy timeout, YouTube 429, invalid URL.
Финальный вывод
Сервис транскрибации YouTube-видео — это не столько про Python-библиотеку, сколько про правильный выход к YouTube.
Без residential/mobile proxy решение будет ломаться на cookies, VPN, 429 и bot checks. С residential proxy оно становится рабочим микросервисом, который можно дать как людям, так и агентам.