Полноценный YouTube-клиент для терминала: TUI-сетка с превью, воспроизведение в 1080p, рекомендации, поиск, SponsorBlock, статистика просмотров — без браузера, без yt-dlp в пути воспроизведения.

YouHub — это терминальный YouTube, работающий целиком из консоли. Вместо браузера — kitty-терминал с сеткой превью, вместо веб-плеера — кастомный ffplay-yt (форк FFplay из FFmpeg 4.3.9 с IPC-расширениями). Видеопоток получается напрямую через реверс-инженерный протокол SABR (Server ABR) YouTube, без посредников вроде yt-dlp.
ffplay-yt с боковой панелью рекомендаций, переключением скорости (1×/1.5×/2×), анимированными оверлеямиПри нажатии Enter открывается окно ffplay-yt с видео и боковой панелью рекомендаций (Tab).
fonts-dejavu в Debian)git clone https://github.com/HelpFreedom/youthub.git
cd youthub
python3.11 -m venv .venv
source .venv/bin/activate
python3.11 -m pip install curl_cffi httpx Pillow
npm install
Это установит youtubei.js, googlevideo, bgutils-js, jsdom, canvas и выполнит post-install патч (scripts/patch_googlevideo.mjs).
ffplay-yt — это минимальная кастомная сборка FFplay из FFmpeg 4.3.9 с IPC-расширениями. Патченный исходник лежит в ffplay-yt/ffplay.c.
# Скачиваем исходники FFmpeg 4.3.9
mkdir -p ffplay-yt/src && cd ffplay-yt/src
apt source ffmpeg=7:4.3.9-0+deb11u2
# Или: wget https://ffmpeg.org/releases/ffmpeg-4.3.9.tar.xz && tar xf ffmpeg-4.3.9.tar.xz
cd ffmpeg-4.3*
# Подставляем патченный ffplay.c
cp ../../ffplay.c fftools/ffplay.c
# Конфигурируем минимальную сборку
./configure \
--disable-everything \
--enable-gpl --enable-version3 \
--enable-decoder=h264,vp9,libdav1d,opus,aac,mp3,mjpeg,png \
--enable-demuxer=matroska,mov,webm \
--enable-protocol=file,pipe,unix,fd \
--enable-filter=aresample,scale,atempo,volume \
--enable-parser=h264,vp9,opus,aac \
--enable-libdav1d --enable-libopus \
--enable-sdl2 --enable-ffplay \
--enable-indev=alsa \
--disable-doc --disable-htmlpages --disable-manpages \
--disable-ffmpeg --disable-ffprobe
# Сборка
make ffplay -j$(nproc)
# Копируем бинарник
mkdir -p ../../bin
cp ffplay ../../bin/ffplay-yt
cd ../../..
Зависимости сборки (Debian/Ubuntu):
sudo apt install build-essential nasm \
libsdl2-dev libdav1d-dev libopus-dev \
libavcodec-dev libavformat-dev libswscale-dev libavutil-dev
sudo apt install xdotool wmctrl fonts-dejavu
Если используете прокси, экспортируйте переменную:
export HTTPS_PROXY="socks5://127.0.0.1:1080"
# Запуск в kitty-терминале
.venv/bin/python3.11 grid_demo.py
При первом запуске YouHub попросит авторизоваться через TV-пейринг:
XXXX-XXXX)Токены сохраняются в cache/oauth.json (chmod 600). Повторный пейринг нужен только если refresh token протухнет.
Для атрибуции просмотров (чтобы видео попадали в историю YouTube) нужны cookie из Firefox:
# Экспортируйте cookie из Firefox
python3.11 extract_cookies.py
# Cookie сохраняются в cache/yt_cookies.txt (chmod 600)
Отключить watchstats: export WATCHSTATS_COOKIES=0
| Клавиша | Действие |
|---|---|
←↑↓→ / hjkl |
Навигация по тайлам |
Enter |
Воспроизведение выбранного видео |
f / а |
Поиск по YouTube |
r / к |
Обновить ленту |
PgUp / PgDn |
Постраничная прокрутка |
Home |
В начало |
q / й |
Выход |
| Клавиша | Действие |
|---|---|
← / → |
Перемотка ±5 сек |
↓ / ↑ |
Перемотка ±30 сек |
Space |
Пауза |
1 |
Скорость 1× |
2 |
Скорость 2× |
3 |
Скорость 3× |
Tab |
Открыть/закрыть панель рекомендаций |
q / Esc |
Выход из плеера |
m |
Mute |
9 / 0 |
Громкость −/+ |
| Скрипт | Назначение |
|---|---|
bin/yt-kill-video |
Принудительно закрыть окно ffplay (для привязки к dwm keybind) |
bin/yt-toggle-overlay |
Скрыть/показать окно ffplay (map/unmap, аудио продолжает играть) |
grid_demo.py (TUI-сетка — kitty graphics protocol)
│
│ Enter → воспроизведение
▼
bridge_player.py (оркестратор)
│
├──→ sabr_bridge.mjs (Node.js SABR-мост)
│ │
│ ├──→ po_token.mjs (PO Token: bgutils-js + jsdom)
│ ├──→ pr_fetch.py (playerResponse: curl_cffi, 24 стратегии)
│ ├──→ youtubei.js (дешифровка n-параметра)
│ └──→ googlevideo (SabrStream → fMP4-сегменты)
│ │
│ ▼
│ ffmpeg (мультиплексор) → /tmp/ytlive_<id>.mkv
│
├──→ ffplay-yt (плеер: SDL2 + IPC)
│ │
│ └──→ Unix socket (POS, SEEK_REQ, OPEN, QUIT, RECS_ITEM...)
│
├──→ recs_pipeline.py (рекомендации для боковой панели)
├──→ sponsorblock.py (авто-пропуск спонсорских вставок)
└──→ watchstats.py (пинги статистики просмотра)
pr_fetch.py получает ytInitialPlayerResponse с YouTube через curl_cffi (реальные TLS-отпечатки Chrome/Firefox/Safari), обходя бот-стенуpo_token.mjs генерирует PO Token (Proof of Origin) через BotGuard-чаллендж в jsdom (без браузера)sabr_bridge.mjs использует youtubei.js для дешифровки URL и googlevideo SabrStream для потоковой загрузки H.264+Opus через протокол SABRffmpeg мультиплексирует видео и аудио в растущий .mkv-файлffplay-yt воспроизводит этот файл, общаясь с bridge_player.py через IPC-сокетYouTube блокирует автоматизированные запросы по TLS-отпечатку (JA3/JA4). Система ротации в pr_fetch.py использует 24 стратегии:
chrome131, chrome145, firefox133, safari184, safari180_ios, chrome131_android, edge101) + 5 InnerTube POST (ANDROID_VR, IOS, MWEB, TVHTML5, WEB_EMBEDDED_PLAYER)HTTPS_PROXYСостояние ротации сохраняется между сессиями в cache/strategy_state.json.
| Файл | Назначение |
|---|---|
grid_demo.py |
Главная точка входа — TUI-сетка с навигацией |
bridge_player.py |
Оркестратор воспроизведения (мост, плеер, SponsorBlock, рекомендации) |
sabr_bridge.mjs |
Персистентный Node.js SABR-стриминг-мост |
pr_fetch.py |
Получение playerResponse с ротацией TLS-стратегий |
po_token.mjs |
Генерация PO Token без браузера (bgutils-js + jsdom) |
po_minter.mjs |
Чеканка PO Token из готового integrity token |
sponsorblock.py |
Клиент SponsorBlock API (авто-пропуск спонсорских вставок) |
recs_pipeline.py |
Получение и рендеринг рекомендаций для боковой панели |
watchstats.py |
Пинги статистики просмотра (история YouTube) |
auth_cookies.py |
Cookie-аутентификация с SAPISIDHASH |
youthub/| Файл | Назначение |
|---|---|
innertube.py |
Клиент InnerTube API (TVHTML5): home, search, next, player |
auth.py |
OAuth 2.0 Device Grant (TV-пейринг) |
feed.py |
Парсер ответов InnerTube в структуры Video/Shelf/Feed |
feed_loader.py |
Движок бесконечной прокрутки |
graphics.py |
Kitty Graphics Protocol (передача PNG в терминал) |
terminal.py |
Размер терминала, KeyReader (raw-ввод + UTF-8) |
thumbnails.py |
Дисковый кэш превью с параллельной предзагрузкой |
preview.py |
Слайд-шоу при наведении (4 кадра с кроссфейдами) |
| Файл | Назначение |
|---|---|
ffplay-yt/ffplay.c |
Патченный FFplay: IPC-сокет, боковая панель, оверлеи, анимации |
| Файл | Назначение |
|---|---|
sabr.py |
Чисто-питоновский клиент протокола SABR (research/fallback) |
proto_edit.py |
Round-trip библиотека для Protobuf |
proto_dump.py |
Инспектор Protobuf-сообщений |
bootstrap.py |
Browser-based bootstrap (legacy, для отладки) |
extract_cookies.py |
Экспорт cookie из Firefox |
scripts/patch_googlevideo.mjs |
Post-install патч для npm-пакета googlevideo |
| Файл | Назначение |
|---|---|
ump_extract.py |
Извлечение медиа-треков из UMP-потока |
ump_inspect.py |
Инспектор UMP-сообщений |
probe_potoken.py |
Диагностика PO Token |
protocol_dump.py |
Запись сетевого трафика YouTube |
verify_history.py |
Проверка попадания видео в историю YouTube |
| Файл | Назначение |
|---|---|
test_innertube.py |
Тест InnerTube API + OAuth-пейринг |
test_tvpair.py |
Тест TV Device Grant (проверка client_id) |
test_cookieexchange.py |
Тест обмена cookie |
test_cookieping.py |
Тест пинга с cookie |
test_logevent.py |
Тест отправки событий |
| Файл | Назначение |
|---|---|
player.py |
Первый плеер (yt-dlp + ffplay) |
tui_player.py |
Видео целиком в терминале через kitty graphics |
stream_player.py |
Стриминг через FIFO (заменён bridge_player) |
live_stream.py |
Стриминг live-трансляций через Playwright |
Все кэши хранятся в cache/ (создаётся автоматически):
| Файл | TTL | Описание |
|---|---|---|
oauth.json |
до отзыва | OAuth-токены (chmod 600) |
yt_cookies.txt |
до протухания | Cookie Firefox (chmod 600) |
strategy_state.json |
постоянно | Состояние ротации TLS-стратегий |
innertube_home.json |
120 сек | Кэш домашней ленты |
innertube_next.json |
120 сек | Кэш /next pivot |
pr_<vid>.json |
12 часов | Кэш playerResponse |
sponsorblock_<vid>.json |
24 часа | Кэш сегментов SponsorBlock |
thumbnails/<vid>.jpg |
бессрочно | Превью-изображения |
recs_thumbs/<vid>.jpg |
бессрочно | Превью для боковой панели |
recs_text/<vid>.png |
бессрочно | Текстовые полоски рекомендаций |
Кастомный ffplay-yt включает несколько анимированных оверлеев, рендерящихся через SDL2:
Полупрозрачная капсула с текущей скоростью (1.0×, 1.5×, 2.0×) — ease-out-back анимация с «пружинящим» эффектом, длительность 0.85 сек.
Панель рекомендаций выезжает справа с ease-out-cubic анимацией (0.22 сек). Видео плавно сдвигается влево.
Два шеврона (▶▶ / ◀◀) с текстом (+5s, -30s) — fade-in/fade-out, 0.7 сек.
Цветная метка категории + «skipped Xs» внизу экрана — выезд снизу, 1.6 сек. Цвет зависит от типа: sponsor=зелёный, selfpromo=жёлтый, intro=циановый, outro=синий и т.д.
Все оверлеи рендерятся через встроенный 3×5 bitmap-шрифт (SPEED_GLYPH[16][5]) и примитивы SDL_RenderFillRect (совместимость с SDL 2.0.14, без SDL_RenderGeometry).
googlevideo npm-пакет/api/stats/watchtime с SAPISIDHASH$ claude mcp add youthub \
-- python -m otcore.mcp_server <graph>