youtube-publish
Публикует готовый контент на личный YouTube-канал Антона [аккаунт]: вычерпывает новое из NotebookLM и очереди, рендерит аудио в видео, пишет описание с фрагментом расшифровки для SEO и авторским блоко
Files2
View on GitHubSKILL.mdskills/youtube-publish/SKILL.md
GitHub/youtube-publish — контент на личный канал Антона
🧒 Доклад Антону кончается блоком «Простыми словами» ([[eli5-always]]). Описания видео — без ELI5, у них свой голос.
Родился 21.09.2026 из голосовой Антона (tg:-[id], 08:34 UTC). Дословно: «Публиковать разный контент на личный YouTube-канал. Один из основных источников контента сейчас — NotebookLM... Как только появляется новый материал из NotebookLM, его нужно выкладывать на YouTube... Делать качественное описание к видео: что это за видео, о чём оно, зачем создано. Добавлять небольшой транскрипт или фрагмент транскрипта для лучшего SEO/GEO. Сделать так, чтобы видео легко находилось через поиск. Обязательно добавлять авторский контекст от Антона.»
Цель контента (его слова): обучение Антона · подготовка к трудоустройству в LLM-компанию · публичный YouTube-след вокруг LLM/AI-тем.
Склейка с другими скиллами (он прямо просил «будет склеиваться»): контракт односторонний.
Производители (/teach-podcast, /notebooklm, /episode, любая будущая труба) кладут строку
в очередь $OBSIDIAN_VAULT/05-Resources/NotebookLM/_youtube-queue.md. Этот скилл вычерпывает
её и больше ничего от них не требует. Производитель забыл строку — не беда: движок делает и
свободный скан папок артефактов, файл на диске потерять нельзя (замер 21.09: очередь знала 14
файлов, на диске лежало 35).
Канал — доказан, не угадан
[аккаунт] · UCa_lk_0LTagC9tI6f9NRF_w · «Tony» · 305 подписчиков · 75 видео · с 18.03.2011.
Три независимые улики (21.09.2026):
- YouTube Data API v3 по id из истории браузера вернул title/customUrl/статистику.
browser_history.db: Антон сам открывал Студию этого канала 30.08 и 02.09 из Firefox-профиляm26xxxkf.anton-clean— значит и канал его, и живой логин лежит именно там.- На канале уже висят его ролики «моя работа с Claude Code» (30–31.08.2026) — та же тематика.
⚠️ Другого личного канала не искали шире истории браузера. Появится второй — уточнить у Антона, а не выбирать самому.
⛔ Законы
- Описание несёт ЧЕТЫРЕ обязательных куска (прямой список Антона): (а) о чём видео; (б) зачем создано; (в) фрагмент расшифровки для поиска; (г) авторский контекст от него. Нет любого из четырёх — описание бракованное, публиковать нельзя.
- Авторский блок пишется от ПЕРВОГО лица Антона и называет Mycroft. Его формулировка: «Это видео от Антона, оно сгенерировано моим кофаундером Mycroft». Раскрытие ИИ живёт внутри этого блока — плашки «AI-generated» отдельно не ставим (§3.3, DR-701: планка = review control + названное ответственное лицо, а лицо здесь Антон).
- Публичность по умолчанию, приватное не едет. Гейт
privacy_scanперед каждой загрузкой: почта, имена рекрутёров, вилки денег из переписки, секреты. Сработал — видео пропускается, строка в леджерblocked-privacy, чиним руками. Наш собственный calendly/WhatsApp/github — не утечка, это подпись (§1.1). - Подпись обязательна. Футер = блоки C+E из
$IMPORTS_ROOT/content-factory/_STYLE-footer.mdплюс Linktree иgithub.com/tonydzi. YouTube = публичная поверхность, карве-аутов нет. EN-описание НЕ упоминает RU-каналы (правило [коллега] 07.07). - Язык описания = язык дорожки. Суффикс
-ru/-enв имени файла — истина. RU-эпизод с английским описанием = брак: YouTube так и проиндексирует. - Находимость строится тремя рычагами, не тегами одними: (1) субтитры из расшифровки
(
captions.insert) — YouTube индексирует их текст целиком и это сильнее любых тегов для аудио-контента; (2)defaultLanguage/defaultAudioLanguageпроставлены; (3) первые три тега продублированы хэштегами — они показываются над заголовком. - Готово = прочитано обратно. После загрузки движок сам делает
videos.listи печатает реальныйprivacyStatus. Код ответа 200 доказательством публикации не является (§4.3-бис). - Выдумывать нельзя. Описание собирается из очереди, пакета урока и расшифровки. Нет данных — пишем короче, а не сочиняем содержание, которого в видео нет.
- Суточный потолок 5 видео — не вкус, а арифметика квоты: 1600 (videos.insert) + 400
(captions.insert) + 1 (videos.list) ≈ 2001 единица, суточная квота проекта 10 000.
Потолок зашит в
DAILY_CAPи проверяется регрессом. - Публикуем только вычитанное.
runпо умолчанию берёт ТОЛЬКО строки очереди — у них человеческий заголовок. Файлы свободного скана видны вscan, но наружу без флага--include-unqueuedне идут: заголовок «Hedge 01 inside», собранный из имени файла, на публичном канале Антона выглядит браком. Нужен нормальный заголовок — строку пишет производитель (/teach-podcast), а не этот скилл на глаз. - Серия выходит по номерам, а не по дате файла. «(урок 3)» в заголовке — ключ сортировки. Урок 15 сделан позже урока 1, но и выйти должен позже.
Рельсы: две, и выбор между ними ЗАМЕРЯЕТСЯ
Рельса A — API (основная, безлюдная)
videos.insert resumable + captions.insert. Всё ставится одним вызовом: заголовок, описание,
теги, категория 27 (Education), язык, видимость. Только эта рельса умеет работать ночью без человека.
⚠️ РИСК, КОТОРЫЙ ОБЯЗАН БЫТЬ ЗАМЕРЕН, А НЕ ПРЕДПОЛОЖЕН. У GCP-проектов, не прошедших
аудит YouTube API, загруженные через API видео могут запираться в private без возможности
открыть их руками. Это заявленное поведение платформы, и оно ломает всю цель («публичный след»).
Поэтому run по умолчанию идёт канарейкой --limit 1 и сразу читает статус обратно:
privacyStatus == public→ рельса A рабочая, дальше можно пачкой;- иначе → движок сам останавливается с кодом 2 и печатает вердикт; переходим на рельсу B.
⛔ До канарейки запрещено говорить «рельса A работает» — это claim (§5.4).
Рельса B — браузер (фолбэк и первый боевой заход)
Firefox, профиль m26xxxkf.anton-clean (там живой логин канала), studio.youtube.com.
Команда manifest печатает и кладёт готовый пакет: путь к mp4 + заголовок + описание + теги
- видимость. Дальше Create → Upload videos → перетащить mp4 → вставить поля из манифеста. Ban-риска нет: это собственный канал Антона, а не чужая площадка.
Шаг 0. RECALL и состояние
python ~/.claude/scripts/youtube_publish.py scan # что ждёт, сколько уже ушло сегодня
python ~/.claude/scripts/youtube_publish.py ledger # что уже на канале
python ~/.claude/scripts/youtube_publish.py auth check # жива ли рельса A
Леджер ~/.claude/state/youtube_ledger.jsonl — единственный источник «что уже выложено».
Статус в очереди волта — подсказка человеку: очередь под Syncthing, её правят три узла, у неё
уже есть sync-conflict-копии. Сторож не живёт в том, что сторожит ([[watchdog-must-verify-the-item]]).
Шаг 1. Посмотреть описание ГЛАЗАМИ до публикации
python ~/.claude/scripts/youtube_publish.py meta <имя файла>
Печатает ровно то, что увидит зритель, плюс счётчики лимитов (заголовок/100, описание/5000, теги/500) и вердикт гейта приватности. Первый эпизод серии смотрим глазами всегда — дальше шаблон уже проверен и можно пачкой.
Сомнение по вкусу текста → /taste-check <файл описания>.
Шаг 2. Рендер (аудио → видео)
YouTube не принимает голый .m4a, нужен видеоконтейнер.
python ~/.claude/scripts/youtube_publish.py render <имя файла>
Делает обложку 1920×1080 (тёмная карточка: строка серии, заголовок в 5 строк, [аккаунт],
Palo Alto AI Research Lab · NotebookLM) и склеивает с дорожкой: h264, 2 к/с, aac 192k,
+faststart. Замер 21.09: эпизод 18 минут → 26,5 МБ за 2 минуты на хабе.
Шаг 3. Заливка
python ~/.claude/scripts/youtube_publish.py run --limit 1 # КАНАРЕЙКА, первый раз всегда так
python ~/.claude/scripts/youtube_publish.py run --limit 5 # рабочий заход после зелёной канарейки
python ~/.claude/scripts/youtube_publish.py run --dry-run # посмотреть план без действий
python ~/.claude/scripts/youtube_publish.py run --include-unqueued # редко: и файлы без строки
Порядок внутри: гейт приватности → рендер → субтитры (faster-whisper локально, 0 токенов) → загрузка → чтение статуса обратно → строка в леджер → отметка в очереди (best-effort). Падение на любом шаге пишет причину в леджер и не помечает файл сделанным: упавший эпизод остаётся в очереди, иначе он теряется молча.
Рельса A мертва или заперла видео → рельса B:
python ~/.claude/scripts/youtube_publish.py manifest --limit 5
Шаг 4. Проверка (§4.3-бис, второй заход другим методом)
verify <videoId>—privacyStatus,uploadStatus, причина отказа, если есть.- Открыть
https://youtu.be/<id>и посмотреть глазами: звук идёт, обложка та, описание не обрезано на середине блока. - Через сутки: видео находится поиском по своему заголовку? Нет — значит находимость не построена, и это отдельная починка, а не «ну бывает».
- Итог пишется парой: «проверено: … · НЕ проверено: … (почему)».
Шаг 5. Учёт и связи (тем же заходом)
- Леджер пишется движком сам.
- Строка в очереди → «выложено <дата> <ссылка>» (движок делает best-effort).
_teach-curriculum.md: статус урока → «на YouTube» + ссылка.- Ссылка уходит обратно в учебный контур: в шпаргалку
/interview-prepтого звонка, к которому урок, — строка «Подкаст: <ссылка>». - Контент-майнер ([[content-miner-reflex]]): вышел эпизод — черновик тизера в воронку. Один эпизод = один тизер, не веер (§1.5 «спам ≠ громкость»).
- Вердикт о скилле одной строкой: 🔧 обновляю / 📝 обновлю не сейчас / ✅ не надо, с причиной (§9.3-бис). Рутина судит ещё и собственную инструкцию (§9.3-бис поправка 13.09).
Рутина
youtube-publish-drip — ночное окно 23:00–06:00 Лиссабон (§4.6), раз в сутки, --limit 5.
Почему ежедневно, а не «раз в 2–3 дня», как допускал Антон: суточная квота всё равно режет
до 5 видео, а ежедневный мелкий заход ровнее ложится на производство (/teach-podcast делает
1–5 эпизодов в день). Накопилось больше — само растянется на несколько ночей.
⛔ Рутина НЕ поднимается из сессии, порождённой scheduled-task (§9.2): сид кладётся в
spawn_queue.py, живая сессия его поднимает.
Темп режем по §4.5-тер, если хаб начнёт тормозить.
Грабли, оплаченные при рождении (21.09.2026)
- Порядок чисток в описании. Черновики очереди кончаются фразой «Контакты и CTA — из
_STYLE-footer.md». Снимать надо СНАЧАЛА бэктики, ПОТОМ фразу: иначе точка внутри_STYLE-footer.mdобрывает замену и в публичное описание уезжает хвост «md.». Поймано регрессомtest_internal_vault_paths_never_leak`, который на этом краснел. - Тайм-коды в расшифровке. Дорожки NotebookLM идут с
[00:03]на каждой фразе. В описании это мусор: YouTube индексирует слова, а не скобки. Движок их снимает. - Расшифровка машинная и с опечатками («менажмент», «искусном интеллекте»). Правим не текст, а честность: фрагмент помечен «машинная, с опечатками». Вычитывать чужую ASR-дорожку ради описания — работа не по цене.
- Очередь знает не всё. 14 строк в очереди против 35 файлов на диске. Поэтому скан свободный, а не только по очереди.
- Голый
.m4aYouTube не берёт — всегда рендер в mp4.
Потребители
- Антон —
/yt, «выложи на ютуб», «что ждёт выгрузки». /teach-podcastШаг 6.3 — кладёт строку в_youtube-queue.md; этот скилл её вычерпывает./notebooklm— любой артефакт, который не стыдно показать миру, идёт той же дорогой.- рутина
youtube-publish-drip— ночной вычерп.
Двери и файлы
- Движок:
~/.claude/scripts/youtube_publish.py(док внутри файла, §5.8). - Регресс:
~/.claude/scripts/_test_youtube_publish.py— 28 проверок, kill-list в шапке. - Леджер:
~/.claude/state/youtube_ledger.jsonl. - Рендер и манифест:
~/.claude/state/youtube_render/. - Очередь:
$OBSIDIAN_VAULT/05-Resources/NotebookLM/_youtube-queue.md. - Токен рельсы A:
<secrets>/youtube_token_tonydzz.json(в волт и в always-loaded не кладём, §3.4).
Канон: CLAUDE.md §9.13 (учим Антона подкастом), §1.1 (подпись в каждом касании), §3.3 (голос и раскрытие), §3.2 (публичные каналы), §5.7 (shadow-first), §4.3-бис (готово = второй заход). Память: [[teach-anton-by-podcast-default]], [[youtube-publish-channel-and-rails]], [[everything-becomes-content]], [[outbound-signature]].
<!--kit-footer-->Like this skill? It is part of second-brain-starter-kit: the second brain we built for ourselves and run every day at Palo Alto AI Research Lab. Install the whole set with npx skills add tonydzi/second-brain-starter-kit. Everything is open source and free, so take what you need.
Flagships worth a look on their own: secondop-panel (a second opinion from a panel of external models), claude-memory-tidy (stop your agent's memory from rotting), telegram-mcp-kit (your own Telegram over MCP in about 15 minutes).
Author: Anton Dziatkovskii, Palo Alto AI Research Lab. Telegram @tonydzi - WhatsApp +1 341 222 9178 - X @Tony_Stef_
Engineers: want to test-drive this setup? Message me. I hand out free starter seeds to engineers who test and report back, and custom skill requests are welcome.