Как ИИ мне со стримом на Ютубе «помогал»
Решил с понедельника открыть сезон стримов на Ютубе. Идея банальная: показывать вживую, как я проектирую и вайбкодю пет-проект. Ну как пет-проект… В мае ему уже исполнится год и по архитектуре и функциям он разросся настолько, что уже приходится относиться к нему со всем уважением :)
Пошёл в ChatGPT, поделился идеей. «Идея замечательная!» — сказал чат и начал уже было расхваливать меня, но я его остановил. «Мне нужна помощь с OBS: хочу сделать в стриме плашку с информацией: кто я, что прямо сейчас делаю, ссылки, время, вот это всё». «Спокойствие, сейчас всё объясню!» — сказал ChatGPT — и именно с этого началась моя история.
OBS — это программа, популярная среди стримеров. Она позволяет захватывать видео с экрана, вебку, управлять всем этим делом с помощью сцен и транслировать результат, куда прикажете. И, главное, она бесплатная.
Я описал ЧатуГПТ свой сетап:
-
Три монитора и вебкамера;
-
На левом и центральном мониторе я буду работать и стримить;
-
На правом у меня будет висеть OBS и всё, что я не хочу показывать (Телеграм, файлы с заметками, дополнительное окно браузера и т.п.).
И объяснил, какие мне нужны сцены:
-
Левый монитор плюс моя вебка в правом нижнем углу;
-
Центральный монитор плюс моя вебка в правом нижнем углу;
-
Вебка на весь экран — когда я хочу отвлечься от работы и поделиться какой-нибудь офигительной историей.
«И вот для каждой из этих сцен мне нужна сверху плашка, на которой будут…»
-
Имя стримера (моё имя, Егор Камелев);
-
Информация о том, какой сейчас транслируется монитор (или вебка);
-
Редактируемая текстовая информация о том, что именно происходит на стриме прямо сейчас;
-
Ссылки на продвигаемые ресурсы;
-
Дата и время (чтобы можно было понять — это живой стрим или запись).
Кстати, изначально ЧатГПТ сам мне предложил содержимое плашки, но оно не было похоже на то, которое я только что описал выше. Он советовал показывать статус стрима «Live» и его название. Мне эта идея не приглянулась. На мой вопрос «Зачем показывать статус стрима, если его потом всё равно многие будут смотреть в записи?» он не нашёлся что ответить.
В общем, с горем пополам определились с содержимым плашки — и чат начал выдавать мне инструкцию.
-
Сначала сделай чёрную полупрозрачную подложку (а должен заметить, что я пользуюсь Виндой и решил спрятать нижний бар с пуском, поиском и прочими иконками; высота бара составляет 48 пикселей, и именно такую высоту я и задал для плашки; естественно, полупрозрачность там была абсолютно не нужна, т.к. я обрезал транслируемый экран на эту высоту);
-
Теперь добавь текстовый блок с твоим именем. Настрой его. Молодец;
-
Теперь добавь текстовый блок с указанием источника стрима.
В этот момент я прерываю чат. Я бы хотел сделать вертикальные разделители между элементами. Куда мне их лучше засунуть? В качестве отдельных элементов или как часть каждого следующего элемента? Чат посоветовал второе.
-
Теперь давай добавим текст про тему стрима. В качестве источника возьмём текстовый файл на твоём компе. Ты сможешь редактировать его и как только сохранишь — текст обновится.
«О, это круто», — подумал я. Но тут увидел, что вертикальные разделители как-то странно висят в воздухе относительно текстов и мне это не понравилось. Поэтому пришлось для каждого разделителя добавлять отдельный элемент сцены.
-
Теперь добавим блок со ссылками. Молодец
-
А теперь часики. Для того, чтобы отображать реальное время мы можем пойти двумя путями:
-
Первый: используем текстовый файл, в котором ты будешь периодически менять время вручную (в этом месте инструкции нужно было видеть моё лицо);
-
Второй: используем такой источник данных как «Браузер» и сделаем html-страничку и небольшой js, который будет автоматически обновлять время.
-
И я такой в этом моменте: «Чегоооо?».
После этого я спросил: «Я правильно понимаю, что мы можем сверстать любой html, использовать свой css и js — и транслировать это в стрим, даже не открывая браузера?». «Да» — ответил ChatGPT.
И в этот момент я понял, что вся моя предыдущая работа с кучей элементов со своими настройками — полностью перечёркивается.
— А почему же ты мне тогда не предложил сразу сверстать плашку целиком?
— Я не знал, сможешь ли ты разобраться с вёрсткой — и предложил самый простой и быстрый вариант для новичка.
— А зачем мне разбираться с вёрсткой, если ты сам отлично всё можешь верстать, и от тебя потребовалась бы лишь инструкция о том, как мне прикрутить это дело к OBS?
— Да, конечно, ты совершенно прав. Так было бы и правильнее, и быстрее!
Пожалуй, это был один из немногих моментов, когда я испытал какие-то эмоции в адрес чатаГПТ.
Через пятнадцать минут всё было готово. Я с помощью чата сверстал три html-ки (для двух мониторов и вебки), связал их с текстовым файлом, в котором мог редактировать тему стрима на лету. Настройки стилей вынес в отдельный файлик. И также создал отдельный файлик для всех скриптов. Проблема висящих разделителей исчезла (как и любая другая проблема, связанная с оформлением). Десять элементов в источниках сцены превратились в один.
Скрипты отвечали за обновление текста, за работу часиков, а также за то, чтобы особенно длинный текст превращался в бегущую строку.
Поделюсь с вами кодом.
Вот overlay для центрального монитора (оверлеи для других сцен — по аналогии):
<!DOCTYPE html>
<html lang="ru">
<head>
<meta charset="UTF-8">
<link rel="stylesheet" href="overlay.css">
</head>
<body data-monitor="center">
<div class="header">
<div class="left">
<span class="name">Егор Камелев</span>
<span class="divider">|</span>
<span class="monitor">
<span id="monitor"></span>
</span>
<span class="divider">|</span>
<span class="comment" id="comment">
<span class="comment-track" id="comment-track">
<span class="comment-text" id="comment-text-1"></span>
<span class="comment-gap"></span>
<span class="comment-text clone" id="comment-text-2"></span>
</span>
</span>
</div>
<div class="right">
<span class="link">t.me/normfreelancer</span>
<span class="divider">|</span>
<span class="link">normcrm.ru</span>
<span class="divider">|</span>
<span id="clock"></span>
</div>
</div>
<script src="overlay.js"></script>
</body>
</html>
Вот css (каскадная таблица стилей):
html, body {
margin: 0;
padding: 0;
width: 1920px;
height: 48px;
overflow: hidden;
}
* {
box-sizing: border-box;
}
body {
font-family: Arial, sans-serif;
font-size: 22px;
line-height: 1;
color: #ffffff;
}
.header {
width: 100%;
height: 100%;
display: flex;
align-items: center;
justify-content: space-between;
padding: 0 18px;
background: linear-gradient(
180deg,
rgba(20,20,22,0.96),
rgba(10,10,12,0.96)
);
overflow: hidden;
}
.left {
display: flex;
align-items: center;
gap: 14px;
min-width: 0;
flex: 1 1 auto;
white-space: nowrap;
overflow: hidden;
}
.right {
display: flex;
align-items: center;
gap: 14px;
flex: 0 0 auto;
margin-left: 24px;
white-space: nowrap;
}
.name {
font-weight: 600;
flex: 0 0 auto;
}
.monitor {
color: #9fd0ff;
flex: 0 0 auto;
}
.comment {
position: relative;
color: #d5d9df;
flex: 1 1 auto;
min-width: 0;
overflow: hidden;
white-space: nowrap;
}
.comment-track {
display: inline-flex;
align-items: center;
white-space: nowrap;
will-change: transform;
}
.comment.marquee .comment-track {
animation: marquee linear infinite;
animation-duration: var(--marquee-duration);
}
.comment-text {
white-space: nowrap;
flex: 0 0 auto;
}
.comment-text.clone {
display: none;
}
.comment.marquee .comment-text.clone {
display: inline-block;
}
.comment-gap {
width: 80px;
flex: 0 0 auto;
}
.link {
color: #7fc1ff;
flex: 0 0 auto;
}
.clock {
color: #ffffff;
flex: 0 0 auto;
}
.divider {
color: rgba(255, 255, 255, 0.35);
flex: 0 0 auto;
font-weight: 400;
transform: translateY(-1px);
}
@keyframes marquee {
from {
transform: translateX(0);
}
to {
transform: translateX(calc(-1 * var(--marquee-shift)));
}
}
А вот весь js (скрипты):
const monitorType = document.body.dataset.monitor;
const monitorText =
monitorType === "left"
? "Источник: монитор слева"
: monitorType === "webcam"
? "Источник: веб-камера"
: "Источник: центральный монитор";
document.getElementById("monitor").textContent = monitorText;
let lastCommentText = "";
async function updateComment() {
try {
const r = await fetch("stream_comment.txt?" + Date.now());
const text = (await r.text()).trim();
if (text === lastCommentText) {
return;
}
lastCommentText = text;
const comment = document.getElementById("comment");
const track = document.getElementById("comment-track");
const text1 = document.getElementById("comment-text-1");
const text2 = document.getElementById("comment-text-2");
const gap = comment.querySelector(".comment-gap");
comment.classList.remove("marquee");
track.style.animation = "none";
text1.textContent = text;
text2.textContent = text;
void track.offsetWidth;
const containerWidth = comment.clientWidth;
const textWidth = text1.scrollWidth;
const gapWidth = gap.offsetWidth;
const shift = textWidth + gapWidth;
if (textWidth > containerWidth) {
const speed = 80;
const duration = shift / speed;
comment.style.setProperty("--marquee-shift", shift + "px");
comment.style.setProperty("--marquee-duration", duration + "s");
track.style.animation = "";
comment.classList.add("marquee");
} else {
comment.style.removeProperty("--marquee-shift");
comment.style.removeProperty("--marquee-duration");
track.style.animation = "";
comment.classList.remove("marquee");
}
} catch {}
}
function updateClock() {
const now = new Date();
const h = String(now.getHours()).padStart(2,'0');
const m = String(now.getMinutes()).padStart(2,'0');
const d = String(now.getDate()).padStart(2,'0');
const mo = String(now.getMonth()+1).padStart(2,'0');
const y = now.getFullYear();
document.getElementById("clock").textContent =
`${h}:${m} ${d}.${mo}.${y}`;
}
setInterval(updateClock,1000);
setInterval(updateComment,1000);
updateClock();
updateComment();
Устанавливается это просто. Создаём папку, кидаем в неё все получившиеся файлы.
В самом OBS добавляем на сцену новый источник (выбираем тип «Browser»), указываем нужную html-ку, задаём высоту и ширину — и готово.
Наверняка можно было придумать решение и получше. Более того, я уверен, что через пару десятков стримов произойдёт несколько оптимизаций. Но для начала оно полностью решает мою задачу.
В итоге понедельничный стрим прошёл отлично. Я и в Axure поработал, и с ChatGPT пообщался, и в Codex повайбкодил, и в свою IDE потаскал результат, и потестировал, и… Да что там — обыкновенный рабочий процесс. Единственный момент: я кучу времени настраивал себе свет, а перед началом стрима забыл его включить.
А за первым стримом прошло ещё два. И я пока не собираюсь останавливаться (поставил себе цель на 100 стримов либо каждый день, либо кроме выходных, ещё не определился). Сегодня, например, буду писать функциональную спецификацию к НормЦРМ и объяснять, как это делается и, главное, зачем.
А про ChatGPT для себя уяснил одну вещь: есть области, в которых он разбирается хорошо и уверенно (например, разработка в стеке Питон + Джанго). А есть те, в которых общие знания у него есть, но здравого смысла, как эти знания применять эффективно и рационально — нет. Потому что, видимо, не было статей, которые разъясняют, как делать — правильно, а как — не очень.
Если бы я сразу сформулировал задачу как «сверстай HTML-оверлей из таких-то параметров» — сэкономил бы две трети времени.
Ссылки на мои Хабростатьи по теме:
Автор: Ekamelev

